1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21 /*! \file o_complex_basic.c
22 * \brief Functions for complex objects
24 * Complex objects are collections of primary objects.
39 #include "libgeda_priv.h"
41 #ifdef HAVE_LIBDMALLOC
45 static int world_get_complex_bounds(OBJECT
*complex,
46 int *left
, int *top
, int *right
, int *bottom
);
47 static void o_complex_recalc(OBJECT
*o_current
);
48 static void o_complex_print(TOPLEVEL
*toplevel
, FILE *fp
, OBJECT
*o_current
,
49 double scale
, GArray
*unicode_table
);
50 static void o_complex_grip_foreach(OBJECT
*o
,
51 gboolean (*fn
)(OBJECT
*o
,
52 int grip_x
, int grip_y
,
56 static void o_complex_embed(TOPLEVEL
*toplevel
, OBJECT
*o
, char yes
);
57 static enum visit_result
58 o_complex_visit(OBJECT
*o_current
,
59 enum visit_result (*fn
)(OBJECT
*, void *), void *context
,
60 enum visit_order order
, int depth
);
61 static OBJECT
*o_complex_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
);
62 static OBJECT
*o_complex_copy_embedded(TOPLEVEL
*toplevel
, OBJECT
*o_current
);
64 /*! Default setting for complex draw function. */
65 void (*complex_draw_func
)() = NULL
;
67 /*! \brief Return the bounds of the given object.
68 * \par Given an object, calculate the bounds coordinates.
69 * \param [in] toplevel The toplevel structure.
70 * \param [in] o_current The object to look the bounds for.
71 * \param [out] rleft pointer to the left coordinate of the object.
72 * \param [out] rtop pointer to the top coordinate of the object.
73 * \param [out] rright pointer to the right coordinate of the object.
74 * \param [out] rbottom pointer to the bottom coordinate of the object.
75 * \return If any bounds were found for the object
76 * \retval 0 No bound was found
77 * \retval 1 Bound was found
79 int world_get_single_object_bounds(OBJECT
*o_current
,
80 int *rleft
, int *rtop
, int *rright
, int *rbottom
)
82 g_return_val_if_fail(o_current
, 0);
84 if (!o_current
->w_bounds_valid
) {
85 o_recalc_single_object(o_current
);
86 if (!o_current
->w_bounds_valid
) {
90 *rleft
= o_current
->w_left
;
91 *rtop
= o_current
->w_top
;
92 *rright
= o_current
->w_right
;
93 *rbottom
= o_current
->w_bottom
;
97 struct get_bounds_context
{
99 int *left
, *top
, *right
, *bottom
;
102 static enum visit_result
get_bounds_one(OBJECT
*o_current
, void *userdata
)
104 struct get_bounds_context
*ctx
= userdata
;
105 int rleft
, rtop
, rright
, rbottom
;
107 if (world_get_single_object_bounds(o_current
, &rleft
, &rtop
, &rright
, &rbottom
)) {
109 *ctx
->left
= min(*ctx
->left
, rleft
);
110 *ctx
->top
= min(*ctx
->top
, rtop
);
111 *ctx
->right
= max(*ctx
->right
, rright
);
112 *ctx
->bottom
= max(*ctx
->bottom
, rbottom
);
116 *ctx
->right
= rright
;
117 *ctx
->bottom
= rbottom
;
125 /*! \brief Return the bounds of the given list of objects.
126 * \par Given a list of objects, calculate the bounds coordinates.
127 * \param [in] complex The list of objects to look the bounds for.
128 * \param [out] left pointer to the left coordinate of the object.
129 * \param [out] top pointer to the top coordinate of the object.
130 * \param [out] right pointer to the right coordinate of the object.
131 * \param [out] bottom pointer to the bottom coordinate of the object.
132 * \return If any bounds were found for the list of objects
133 * \retval 0 No bounds were found
134 * \retval 1 Bound was found
137 world_get_object_list_bounds(OBJECT
*complex, enum list_kind kind
,
138 int *left
, int *top
, int *right
, int *bottom
)
140 struct get_bounds_context context
= {
148 s_visit_list(complex, kind
, &get_bounds_one
, &context
, VISIT_ANY
, 1);
150 return context
.found
;
153 /*! \brief Return the bounds of the given GList of objects.
154 * \par Given a list of objects, calculate the bounds coordinates.
155 * \param [in] head The list of objects to look the bounds for.
156 * \param [out] left pointer to the left coordinate of the object.
157 * \param [out] top pointer to the top coordinate of the object.
158 * \param [out] right pointer to the right coordinate of the object.
159 * \param [out] bottom pointer to the bottom coordinate of the object.
160 * \return If any bounds were found for the list of objects
161 * \retval 0 No bounds were found
162 * \retval 1 Bound was found
164 int world_get_object_glist_bounds(GList
const *head
,
165 int *left
, int *top
, int *right
, int *bottom
)
167 GList
const *s_current
=NULL
;
168 OBJECT
*o_current
=NULL
;
169 int rleft
, rtop
, rright
, rbottom
;
174 /* Find the first object with bounds, and set the bounds variables, then expand as necessary. */
175 while ( s_current
!= NULL
) {
176 o_current
= s_current
->data
;
177 g_assert (o_current
!= NULL
);
178 if (world_get_single_object_bounds(o_current
, &rleft
, &rtop
, &rright
, &rbottom
)) {
180 *left
= min( *left
, rleft
);
181 *top
= min( *top
, rtop
);
182 *right
= max( *right
, rright
);
183 *bottom
= max( *bottom
, rbottom
);
192 s_current
= g_list_next (s_current
);
197 /*! \brief Queries the bounds of a complex object.
198 * \par Function Description
199 * This function returns the bounding box of the complex object
202 * \param [in] complex The complex object.
203 * \param [out] left The leftmost edge of the bounding box (in
205 * \param [out] top The upper edge of the bounding box (in
207 * \param [out] right The rightmost edge of the bounding box (in
209 * \param [out] bottom The bottom edge of the bounding box (in
211 * \return If any bounds were found for the list of objects
212 * \retval 0 No bounds were found
213 * \retval 1 Bound was found
215 static int world_get_complex_bounds(OBJECT
*complex,
216 int *left
, int *top
, int *right
, int *bottom
)
218 g_return_if_fail (complex != NULL
&&
219 (complex->type
== OBJ_COMPLEX
||
220 complex->type
== OBJ_PLACEHOLDER
) &&
221 complex->complex != NULL
);
223 return world_get_object_list_bounds(complex->complex->prim_objs
, LIST_KIND_HEAD
,
224 left
, top
, right
, bottom
);
227 /*! \brief create a new head object
228 * \par Function Description
229 * This function creates a <b>complex_head</b> OBJECT. This OBJECT
230 * is just a special empty object. This object is never modified.
232 * \return new head OBJECT
234 OBJECT
*new_head(TOPLEVEL
*toplevel
)
236 OBJECT
*new_node
=NULL
;
238 new_node
= s_toplevel_new_object(toplevel
, OBJ_HEAD
, "complex_head");
243 char *o_complex_get_refdes(OBJECT
const *o_current
,
244 char const *default_value
)
246 static GHashTable
*objects_warned
= NULL
;
249 /* Casting away const is okay because we don't use return_objects. */
250 refdes
= o_attrib_search_name_single((OBJECT
*) o_current
, "refdes", NULL
);
251 if (refdes
== NULL
) {
252 refdes
= o_attrib_search_name_single((OBJECT
*) o_current
, "uref", NULL
);
254 /* Warn once about a symbol using uref instead of refdes. */
255 if (!objects_warned
) {
256 objects_warned
= g_hash_table_new(&g_direct_hash
, &g_direct_equal
);
257 libgeda_caches
= g_list_prepend(libgeda_caches
, objects_warned
);
259 if (!g_hash_table_lookup_extended(objects_warned
,
260 GINT_TO_POINTER(o_current
->sid
),
262 g_warning(_("Found uref=%s; please use refdes=\n"), refdes
);
263 g_hash_table_replace(objects_warned
,
264 GINT_TO_POINTER(o_current
->sid
),
265 GINT_TO_POINTER(o_current
->sid
));
268 refdes
= default_value
? g_strdup(default_value
) : NULL
;
275 /*! \brief check whether an object is a attributes
276 * \par Function Description
277 * This function checks if an object should be promoted.
278 * An attribute object is promotable if it's promoted by default, or the user
279 * has configured it to promote an attribute.
281 * \param [in] toplevel The TOPLEVEL object
282 * \param [in] object The attribute object to check
283 * \return TRUE if the object is a eligible attribute, FALSE otherwise
285 static int o_complex_is_eligible_attribute (TOPLEVEL
*toplevel
, OBJECT
*object
)
288 int promotableAttribute
= FALSE
;
290 g_return_val_if_fail(object
->type
== OBJ_TEXT
, FALSE
);
292 /* check list against attributes which can be promoted */
293 if (toplevel
->always_promote_attributes
!= NULL
) {
294 if (o_attrib_get_name_value(o_text_get_string(object
), &name
, NULL
)) {
295 if (g_list_find_custom(toplevel
->always_promote_attributes
,
296 name
, (GCompareFunc
) strcmp
) != NULL
) {
297 /* Name of the attribute was in the always promote attributes list */
298 promotableAttribute
= TRUE
;
302 if (promotableAttribute
)
307 /* object is invisible and we do not want to promote invisible text */
308 if (!o_text_printing_p(object
) && toplevel
->promote_invisible
== FALSE
)
309 return FALSE
; /* attribute not eligible for promotion */
311 /* yup, attribute can be promoted */
315 /*! \brief get the embedded state of an complex object
316 * \par Function Description
317 * Checks and returns the status of the complex object.
319 * \param o_current The object to check
320 * \return 1 if embedded, 0 otherwise
322 int o_complex_is_embedded(OBJECT
const *o_current
)
324 g_return_val_if_fail(o_current
!= NULL
, 0);
326 if(o_current
->complex == NULL
)
329 if (o_current
->complex->is_embedded
) {
336 static enum visit_result
get_toplevel_attribs_one(OBJECT
*o_current
,
339 GList
**o_list
= userdata
;
341 if (o_current
->type
== OBJ_TEXT
&&
342 o_current
->attached_to
== NULL
&&
343 o_attrib_get_name_value(o_text_get_string(o_current
), NULL
, NULL
)) {
344 *o_list
= g_list_prepend(*o_list
, o_current
);
350 /*! \brief Get a list of all toplevel attributes of and object list
352 * \par Function Description
353 * Returns a GList of all attribute OBJECTs
355 * \param [in] o_head The head of the object list
356 * \returns A GList of attribute OBJECTs
358 GList
*o_complex_get_toplevel_attribs(OBJECT
*o_head
)
360 GList
*o_list
= NULL
;
362 s_visit_list(o_head
, LIST_KIND_HEAD
, &get_toplevel_attribs_one
, &o_list
,
363 VISIT_DETERMINISTIC
, 1);
364 o_list
= g_list_reverse (o_list
);
370 /*! \brief Get attributes eligible for promotion from inside a complex
372 * \par Function Description
373 * Returns a GList of OBJECTs which are eligible for promotion from
374 * within the passed complex OBJECT.
376 * If detach is TRUE, the function removes these attribute objects from
377 * the prim_objs of the complex. It detached, the returned OBJECTs are
378 * isolated from each other, having their next and prev pointers set to NULL.
380 * If detach is FALSE, the OBJECTs are left in place. Their next and prev
381 * pointers form part of the complex's prim_objs linked list.
383 * \param [in] toplevel The toplevel environment.
384 * \param [in] object The complex object being modified.
385 * \param [in] detach Should the attributes be detached?
386 * \returns A linked list of OBJECTs to promote.
388 GList
*o_complex_get_promotable (TOPLEVEL
*toplevel
, OBJECT
*object
, int detach
)
390 GList
*promoted
= NULL
;
395 if (!toplevel
->attribute_promotion
) /* controlled through rc file */
398 attribs
= o_complex_get_toplevel_attribs(object
->complex->prim_objs
);
400 for (iter
= attribs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
403 /* Is it an attribute we want to promote? */
404 if (!o_complex_is_eligible_attribute(toplevel
, tmp
))
408 /* Remove and isolate it from the complex list */
409 s_basic_unlink_object(tmp
);
410 tmp
->complex_parent
= NULL
;
413 promoted
= g_list_prepend (promoted
, tmp
);
416 g_list_free (attribs
);
418 promoted
= g_list_reverse (promoted
);
422 static enum visit_result
signal_add_object_one(OBJECT
*o
, void *userdata
)
424 PAGE
*page
= userdata
;
426 g_signal_emit_by_name(page
, "add-object", o
);
431 /*! \brief Promote attributes from the passed OBJECT
433 * \par Function Description
434 * Promotes attributes from the passed OBJECT, linking them into the
435 * OBJECT linked list immediately prior to the passed OBJECT.
437 * \param [in] toplevel The toplevel environment.
438 * \param [in] page The PAGE on which \a object exists.
439 * \param [in] object The complex object whose attributes are being promoted.
441 void o_complex_promote_attribs(TOPLEVEL
*toplevel
, PAGE
*page
, OBJECT
*object
)
444 OBJECT
*first_promoted
, *last_promoted
;
446 promoted
= o_complex_get_promotable (toplevel
, object
, TRUE
);
448 if (promoted
== NULL
)
451 /* Link the promoted OBJECTs together */
452 o_glist_relink_objects (promoted
);
454 first_promoted
= promoted
->data
;
455 last_promoted
= g_list_last (promoted
)->data
;
457 /* Insert promoted attributes before the complex in the object list */
458 s_basic_splice(object
, first_promoted
, last_promoted
);
460 /* XXX Emit signal only after the objects are on the page. */
461 s_visit_list(first_promoted
, LIST_KIND_REST
, &signal_add_object_one
, page
,
465 /* Attach promoted attributes to the original complex object */
466 o_attrib_attach_list (toplevel
, promoted
, object
);
468 g_list_free (promoted
);
472 /*! \brief Delete or hide promotable from the passed OBJECT
474 * \par Function Description
475 * Deletes or hides promotable attributes from the passed OBJECT.
476 * This is used when loading symbols during the load of a schematic from
477 * disk. The schematic will already contain local copies of symbol's
478 * promotable objects, so we delete or hide the symbol's copies.
480 * Deletion / hiding is dependant on the setting of
481 * toplevel->keep_invisible. If true, attributes eligible for
482 * promotion are kept in memory but flagged as invisible.
484 * \param [in] toplevel The toplevel environment.
485 * \param [in] object The complex object being altered.
487 void o_complex_remove_promotable_attribs (TOPLEVEL
*toplevel
, OBJECT
*object
)
489 GList
*promotable
, *iter
;
491 promotable
= o_complex_get_promotable (toplevel
, object
, FALSE
);
493 if (promotable
== NULL
)
496 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
497 OBJECT
*a_object
= iter
->data
;
498 g_warn_if_fail(a_object
->type
== OBJ_TEXT
);
499 if (toplevel
->keep_invisible
== TRUE
) {
500 /* Hide promotable attributes */
501 o_text_change(a_object
, NULL
, INVISIBLE
, LEAVE_NAME_VALUE_ALONE
);
503 s_delete (toplevel
, a_object
); /* Delete promotable attributes */
507 g_list_free (promotable
);
510 static void o_complex_notice_destroy(OBJECT
*subject
, void *userdata
)
512 TOPLEVEL
*toplevel
= userdata
;
514 /* Evict all slotted symbols. */
515 s_slot_destroy_complex(toplevel
, subject
);
517 /* Detach from any owning slot. */
518 s_slot_unlink(subject
);
521 static enum visit_result
set_complex_parent_one(OBJECT
*tmp
, void *userdata
)
523 OBJECT
*new_node
= userdata
;
525 tmp
->complex_parent
= new_node
;
532 * \par Function Description
535 OBJECT
*o_complex_new(TOPLEVEL
*toplevel
,
537 int color
, int x
, int y
, int angle
,
538 int mirror
, const CLibSymbol
*clib
,
539 const gchar
*basename
,
542 OBJECT
*new_node
=NULL
;
543 OBJECT
*prim_objs
=NULL
;
544 OBJECT
*new_prim_obj
;
545 int save_adding_sel
= 0;
546 int loaded_normally
= FALSE
;
550 new_node
= s_toplevel_new_object(toplevel
, type
, "complex");
551 g_signal_connect(G_OBJECT(new_node
), "remove",
552 G_CALLBACK(o_complex_notice_destroy
), toplevel
);
555 new_node
->complex_basename
= g_strdup (s_clib_symbol_get_name (clib
));
557 new_node
->complex_basename
= g_strdup (basename
);
560 new_node
->color
= color
;
561 new_node
->selectable
= selectable
;
563 new_node
->complex = g_malloc(sizeof (COMPLEX
));
565 new_node
->complex->angle
= angle
;
566 new_node
->complex->mirror
= mirror
;
567 new_node
->complex->is_embedded
= FALSE
;
569 new_node
->complex->x
= x
;
570 new_node
->complex->y
= y
;
572 new_node
->copy_func
= &o_complex_copy
;
573 new_node
->bounds_recalc_func
= o_complex_recalc
;
574 new_node
->draw_func
= complex_draw_func
;
575 new_node
->psprint_func
= &o_complex_print
;
576 new_node
->grip_foreach_func
= &o_complex_grip_foreach
;
577 new_node
->embed_func
= &o_complex_embed
;
578 new_node
->visit_func
= &o_complex_visit
;
580 /* this was at the beginning and p_complex was = to complex */
581 prim_objs
= new_head(toplevel
);
583 /* get the symbol data */
585 buffer
= s_clib_symbol_get_data (clib
);
588 save_adding_sel
= toplevel
->ADDING_SEL
;
589 toplevel
->ADDING_SEL
= 1; /* name is hack, don't want to */
591 if (clib
== NULL
|| buffer
== NULL
) {
592 OBJECT
*save_prim_objs
;
593 char *not_found_text
= NULL
;
594 int left
, right
, top
, bottom
;
595 int x_offset
, y_offset
;
597 /* filename was NOT found */
598 loaded_normally
= FALSE
;
600 /* save the prim_objs pointer, since the following code modifies it */
601 save_prim_objs
= prim_objs
;
603 /* Put placeholder into object list. Changed by SDB on
604 * 1.19.2005 to fix problem that symbols were silently
605 * deleted by gattrib when RC files were messed up. */
606 new_node
->type
= OBJ_PLACEHOLDER
;
608 /* Mark the origin of the missing component */
609 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
610 DETACHED_ATTRIBUTE_COLOR
,
611 x
- 50, y
, x
+ 50, y
);
612 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
613 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
614 DETACHED_ATTRIBUTE_COLOR
,
615 x
, y
+ 50, x
, y
- 50);
616 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
618 /* Add some useful text */
620 g_strdup_printf (_("Component not found:\n %s"),
621 new_node
->complex_basename
);
622 new_prim_obj
= o_text_new(toplevel
,
623 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
624 x
+ NOT_FOUND_TEXT_X
,
625 y
+ NOT_FOUND_TEXT_Y
, LOWER_LEFT
, 0,
627 VISIBLE
, SHOW_NAME_VALUE
);
628 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
629 g_free(not_found_text
);
631 /* figure out where to put the hazard triangle */
632 world_get_text_bounds(prim_objs
, &left
, &top
, &right
, &bottom
);
633 x_offset
= (right
- left
) / 4;
634 y_offset
= bottom
- top
+ 100; /* 100 is just an additional offset */
636 /* TODO: set buffer to string constant that defines the triangle. */
637 /* add hazard triangle */
638 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
639 DETACHED_ATTRIBUTE_COLOR
,
640 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
641 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
642 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
643 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
644 o_set_line_options(new_prim_obj
, END_ROUND
, TYPE_SOLID
, 50, -1, -1);
645 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
646 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
647 DETACHED_ATTRIBUTE_COLOR
,
648 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
649 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
650 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
651 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500);
652 o_set_line_options(new_prim_obj
, END_ROUND
, TYPE_SOLID
, 50, -1, -1);
653 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
654 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
655 DETACHED_ATTRIBUTE_COLOR
,
656 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
657 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500,
658 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
659 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
660 o_set_line_options(new_prim_obj
, END_ROUND
, TYPE_SOLID
, 50, -1, -1);
661 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
662 new_prim_obj
= o_text_new(toplevel
,
663 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
664 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 270,
665 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 90,
666 LOWER_LEFT
, 0, "!", 18,
667 VISIBLE
, SHOW_NAME_VALUE
);
668 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
669 prim_objs
= save_prim_objs
;
671 /* filename was found */
672 loaded_normally
= TRUE
;
674 /* add connections till translated */
675 o_read_buffer(toplevel
, prim_objs
, buffer
, -1, new_node
->complex_basename
);
679 toplevel
->ADDING_SEL
= save_adding_sel
;
681 /* do not mirror/rotate/translate/connect the primitive objects if the
682 * component was not loaded via o_read
684 if (loaded_normally
== TRUE
) {
686 o_list_mirror_world(0, 0, prim_objs
);
689 o_list_rotate_world(0, 0, angle
, prim_objs
);
690 o_list_translate_world(x
, y
, prim_objs
);
693 new_node
->complex->prim_objs
= prim_objs
;
695 /* XXX s_visit_list skips the OBJ_HEAD node. */
696 set_complex_parent_one(prim_objs
, new_node
);
697 s_visit_list(prim_objs
, LIST_KIND_HEAD
, &set_complex_parent_one
, new_node
,
700 o_complex_recalc(new_node
);
702 s_slot_setup_complex(toplevel
, new_node
);
707 /*! \brief create a new embedded object
708 * \par Function Description
709 * This function creates a new embedded object.
711 * \param [in] toplevel The TOPLEVEL object
712 * \param [in] type The type of the object (usually OBJ_COMLEX)
713 * \param [in] color The color of the object
714 * \param [in] x The x location of the complex object
715 * \param [in] y The y location of the complex object
716 * \param [in] angle The rotation angle
717 * \param [in] mirror The mirror status
718 * \param [in] basename The basic name the embedded was created of
719 * \param [in] selectable whether the object can be selected with the mouse
720 * \return a new complex object
722 OBJECT
*o_complex_new_embedded(TOPLEVEL
*toplevel
,
723 char type
, int color
, int x
, int y
, int angle
, int mirror
,
724 const gchar
*basename
, int selectable
)
726 OBJECT
*prim_objs
=NULL
;
727 OBJECT
*new_node
=NULL
;
729 new_node
= s_toplevel_new_object(toplevel
, type
, "complex");
731 new_node
->complex = g_malloc(sizeof (COMPLEX
));
732 new_node
->complex->x
= x
;
733 new_node
->complex->y
= y
;
735 new_node
->complex->angle
= angle
;
736 new_node
->complex->mirror
= mirror
;
737 new_node
->complex->is_embedded
= TRUE
;
739 new_node
->complex_basename
= g_strdup(basename
);
741 new_node
->color
= color
;
742 new_node
->selectable
= selectable
;
744 new_node
->copy_func
= &o_complex_copy_embedded
;
745 new_node
->bounds_recalc_func
= o_complex_recalc
;
746 new_node
->draw_func
= complex_draw_func
;
747 new_node
->psprint_func
= &o_complex_print
;
748 new_node
->grip_foreach_func
= &o_complex_grip_foreach
;
749 new_node
->embed_func
= &o_complex_embed
;
750 new_node
->visit_func
= &o_complex_visit
;
752 /* this was at the beginning and p_complex was = to complex */
753 prim_objs
= new_head(toplevel
);
754 new_node
->complex->prim_objs
= prim_objs
;
756 set_complex_parent_one(prim_objs
, new_node
);
757 /* XXX Embedded prim_objs will get reparented in o_read_buffer. */
759 /* don't have to translate/rotate/mirror here at all since the */
760 /* object is in place */
764 /*! \brief update the visual boundaries of the complex object
765 * \par Function Description
766 * This function updates the boundaries of the object \a o_current.
768 * \param [in] toplevel The TOPLEVEL object
769 * \param [in] o_current The OBJECT to update
771 static void o_complex_recalc(OBJECT
*o_current
)
773 int left
, right
, top
, bottom
;
775 /* recalc routine Add this somewhere */
777 /* o_recalc(o_current->complex);*/
779 if ((!o_current
) || (o_current
->type
!= OBJ_COMPLEX
&& o_current
->type
!= OBJ_PLACEHOLDER
))
782 if (o_current
->complex->x
== -1 && o_current
->complex->y
== -1) {
783 /* Off-schematic components have no bounding box. */
787 if (world_get_complex_bounds(o_current
, &left
, &top
, &right
, &bottom
)) {
788 o_current
->w_left
= left
;
789 o_current
->w_top
= top
;
790 o_current
->w_right
= right
;
791 o_current
->w_bottom
= bottom
;
792 o_current
->w_bounds_valid
= TRUE
;
796 /*! \brief read a complex object from a char buffer
797 * \par Function Description
798 * This function reads a complex object from the buffer \a buf.
800 * \param [in] toplevel The TOPLEVEL object
801 * \param [in] buf a text buffer (usually a line of a schematic file)
802 * \param [in] release_ver The release number gEDA
803 * \param [in] fileformat_ver a integer value of the file format
804 * \return The new object
806 * \todo Don't use fixed-length string for symbol basename
808 OBJECT
*o_complex_read(TOPLEVEL
*toplevel
,
809 char buf
[], unsigned int release_ver
,
810 unsigned int fileformat_ver
)
817 char basename
[256]; /* FIXME This is a hack */
822 sscanf(buf
, "%c %d %d %d %d %d %s\n",
823 &type
, &x1
, &y1
, &selectable
, &angle
, &mirror
, basename
);
833 s_log_message(_("Found a component with an invalid rotation [ %c %d %d %d %d %d %s ]\n"), type
, x1
, y1
, selectable
, angle
, mirror
, basename
);
843 s_log_message(_("Found a component with an invalid mirror flag [ %c %d %d %d %d %d %s ]\n"), type
, x1
, y1
, selectable
, angle
, mirror
, basename
);
846 if (strncmp(basename
, "EMBEDDED", 8) == 0) {
847 new_obj
= o_complex_new_embedded(toplevel
, type
,
848 WHITE
, x1
, y1
, angle
, mirror
,
852 const CLibSymbol
*clib
= s_clib_get_symbol_by_name (basename
);
854 new_obj
= o_complex_new(toplevel
, type
,
858 basename
, selectable
);
859 /* Delete or hide attributes eligible for promotion inside the complex */
860 o_complex_remove_promotable_attribs (toplevel
, new_obj
);
866 /*! \brief Create a string representation of the complex object
867 * \par Function Description
868 * This function takes a complex \a object and return a string
869 * according to the file format definition.
871 * \param [in] object a complex OBJECT
872 * \return the string representation of the complex OBJECT
874 char *o_complex_save(OBJECT
*object
)
879 g_return_val_if_fail (object
!= NULL
, NULL
);
881 if ((object
->type
== OBJ_COMPLEX
) || (object
->type
== OBJ_PLACEHOLDER
)) {
882 basename
= g_strdup_printf ("%s%s",
883 object
->complex->is_embedded
? "EMBEDDED" : "",
884 object
->complex_basename
);
885 /* We force the object type to be output as OBJ_COMPLEX for both
886 * these object types. */
887 buf
= g_strdup_printf("%c %d %d %d %d %d %s", OBJ_COMPLEX
,
888 object
->complex->x
, object
->complex->y
,
889 object
->selectable
, object
->complex->angle
,
890 object
->complex->mirror
, basename
);
897 /*! \brief move a complex object
898 * \par Function Description
899 * This function changes the position of a complex \a object.
901 * \param [in] dx The x-distance to move the object
902 * \param [in] dy The y-distance to move the object
903 * \param [in] object The complex OBJECT to be moved
905 void o_complex_translate_world(int dx
, int dy
, OBJECT
*object
)
907 g_return_if_fail (object
!= NULL
&&
908 (object
->type
== OBJ_COMPLEX
||
909 object
->type
== OBJ_PLACEHOLDER
));
911 object
->complex->x
= object
->complex->x
+ dx
;
912 object
->complex->y
= object
->complex->y
+ dy
;
914 o_list_translate_world(dx
, dy
, object
->complex->prim_objs
);
916 o_complex_recalc(object
);
919 static void o_complex_print(TOPLEVEL
*toplevel
, FILE *fp
, OBJECT
*o_current
,
920 double scale
, GArray
*unicode_table
)
922 fprintf(fp
, "gsave\n");
923 f_print_objects(toplevel
, fp
,
924 o_current
->complex->prim_objs
,
927 fprintf(fp
, "grestore\n");
930 static void o_complex_grip_foreach(OBJECT
*o
,
931 gboolean (*fn
)(OBJECT
*o
,
932 int grip_x
, int grip_y
,
933 enum grip_t whichone
,
937 const GRIP complex_grips
[] = {
941 .whichone
= GRIP_COMPLEX_ORIGIN
,
943 { .whichone
= GRIP_NONE
}
946 s_basic_grip_foreach_helper(o
, complex_grips
, fn
, userdata
);
949 static void o_complex_embed(TOPLEVEL
*toplevel
, OBJECT
*o
, char yes
)
952 g_assert(o
->type
== OBJ_COMPLEX
|| o
->type
== OBJ_PLACEHOLDER
);
954 if (!o_complex_is_embedded(o
) == !yes
) {
959 /* Set the embedded flag. */
960 o
->complex->is_embedded
= TRUE
;
961 o
->copy_func
= &o_complex_copy_embedded
;
963 s_log_message("Component [%s] has been embedded\n",
964 o
->complex_basename
);
966 const CLibSymbol
*sym
;
968 /* search for the symbol in the library */
969 sym
= s_clib_get_symbol_by_name(o
->complex_basename
);
972 /* symbol not found in the symbol library: signal an error */
973 s_log_message(_("Could not find component [%s], while trying to "
974 "unembed. Component is still embedded\n"),
975 o
->complex_basename
);
979 /* clear the embedded flag */
980 o
->complex->is_embedded
= FALSE
;
981 o
->copy_func
= &o_complex_copy
;
983 s_log_message(_("Component [%s] has been successfully unembedded\n"),
984 o
->complex_basename
);
987 toplevel
->page_current
->CHANGED
= 1; /* FIXME: Do this from an observer. */
990 static enum visit_result
991 o_complex_visit(OBJECT
*o_current
,
992 enum visit_result (*fn
)(OBJECT
*, void *), void *context
,
993 enum visit_order order
, int depth
)
995 g_return_if_fail(o_current
->type
== OBJ_COMPLEX
);
996 g_return_if_fail(o_current
->complex->prim_objs
!= NULL
);
998 return (s_visit_list(o_current
->complex->prim_objs
, LIST_KIND_HEAD
, fn
,
999 context
, order
, depth
));
1002 /*! \brief create a copy of a complex object
1003 * \par Function Description
1004 * This function creates a copy of the complex object \a o_current.
1006 * \param [in] toplevel The TOPLEVEL object
1007 * \param [in] o_current The object that is copied
1008 * \return a new complex object
1010 static OBJECT
*o_complex_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
1012 OBJECT
*new_obj
=NULL
;
1014 const CLibSymbol
*clib
= NULL
;
1016 g_return_val_if_fail(o_current
!= NULL
, NULL
);
1018 if (o_current
->saved_color
== -1) {
1019 color
= o_current
->color
;
1021 color
= o_current
->saved_color
;
1025 * FIXME If the component library path changes at runtime, then a copy
1026 * might not be an identical one. Maybe rather copy prim_objs directly?
1028 clib
= s_clib_get_symbol_by_name (o_current
->complex_basename
);
1030 new_obj
= o_complex_new (toplevel
, o_current
->type
, color
,
1031 o_current
->complex->x
, o_current
->complex->y
,
1032 o_current
->complex->angle
,
1033 o_current
->complex->mirror
,
1034 clib
, o_current
->complex_basename
,
1035 o_current
->selectable
);
1037 /* Delete or hide attributes eligible for promotion inside the complex */
1038 o_complex_remove_promotable_attribs (toplevel
, new_obj
);
1040 o_attrib_slot_copy(toplevel
, o_current
, new_obj
);
1042 /* deal with stuff that has changed */
1044 /* here you need to create a list of attributes which need to be
1045 * connected to the new list, probably make an attribute list and
1046 * fill it with sid's of the attributes */
1051 /*! \brief create a copy of a embedded complex object
1052 * \par Function Description
1053 * This function creates a copy of an embedded complex object \a o_current.
1055 * \param [in] toplevel The TOPLEVEL object
1056 * \param [in] o_current The object that is copied
1057 * \return a new complex object
1059 static OBJECT
*o_complex_copy_embedded(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
1061 OBJECT
*new_obj
=NULL
;
1065 g_return_val_if_fail(o_current
!= NULL
, NULL
);
1067 if (o_current
->saved_color
== -1) {
1068 color
= o_current
->color
;
1070 color
= o_current
->saved_color
;
1073 new_obj
= o_complex_new_embedded (toplevel
, o_current
->type
, color
,
1074 o_current
->complex->x
, o_current
->complex->y
,
1075 o_current
->complex->angle
,
1076 o_current
->complex->mirror
,
1077 o_current
->complex_basename
,
1078 o_current
->selectable
);
1080 /* deal with stuff that has changed */
1082 temp_list
= o_list_copy_all(toplevel
,
1083 o_current
->complex->prim_objs
,
1084 new_obj
->complex->prim_objs
,
1085 toplevel
->ADDING_SEL
);
1087 new_obj
->complex->prim_objs
= return_head(temp_list
);
1089 /* XXX s_visit_list skips the OBJ_HEAD node. */
1090 set_complex_parent_one(new_obj
->complex->prim_objs
, new_obj
);
1091 s_visit_list(new_obj
->complex->prim_objs
, LIST_KIND_HEAD
,
1092 &set_complex_parent_one
, new_obj
, VISIT_ANY
, 1);
1094 o_complex_recalc(new_obj
);
1096 /* here you need to create a list of attributes which need to be
1097 * connected to the new list, probably make an attribute list and
1098 * fill it with sid's of the attributes */
1103 static enum visit_result
set_color_one(OBJECT
*o_current
, void *userdata
)
1105 int *color
= userdata
;
1107 o_current
->color
= *color
;
1109 return VISIT_RES_OK
;
1112 /*! \brief Change the color of an object
1113 * \par Function Description
1114 * This function changes the the color of an object
1116 * \param [in] object The object
1117 * \param [in] color The new color
1119 * \note This function is mainly used to change the color of text objects
1121 void o_complex_set_color(OBJECT
*object
, int color
)
1123 s_visit(object
, &set_color_one
, &color
, VISIT_ANY
, VISIT_DEPTH_FOREVER
);
1126 static enum visit_result
set_color_save_one(OBJECT
*o_current
, void *userdata
)
1128 int const *color
= userdata
;
1130 o_current
->saved_color
= o_current
->color
;
1131 o_current
->color
= *color
;
1133 return VISIT_RES_OK
;
1136 /*! \todo Finish function documentation!!!
1138 * \par Function Description
1141 void o_complex_set_color_save(OBJECT
*o_current
, int color
)
1143 s_visit(o_current
, &set_color_save_one
, &color
, VISIT_ANY
,
1144 VISIT_DEPTH_FOREVER
);
1147 static enum visit_result
unset_color_one(OBJECT
*o_current
, void *userdata
)
1149 o_current
->color
= o_current
->saved_color
;
1150 o_current
->saved_color
= -1;
1152 return VISIT_RES_OK
;
1155 /*! \todo Finish function documentation!!!
1157 * \par Function Description
1160 void o_complex_unset_color(OBJECT
*o_current
)
1162 s_visit(o_current
, &unset_color_one
, NULL
, VISIT_ANY
, VISIT_DEPTH_FOREVER
);
1165 static enum visit_result
set_color_save_only_one(OBJECT
*o_current
,
1168 int const *color
= userdata
;
1170 o_current
->saved_color
= *color
;
1172 return VISIT_RES_OK
;
1175 /*! \todo Finish function documentation!!!
1177 * \par Function Description
1180 void o_complex_set_saved_color_only(OBJECT
*o_current
, int color
)
1182 s_visit(o_current
, &set_color_save_only_one
, &color
, VISIT_ANY
,
1183 VISIT_DEPTH_FOREVER
);
1186 /*! \todo Finish function documentation!!!
1188 * \par Function Description
1191 void o_complex_rotate_world(int centerx
, int centery
,
1192 int angle
, OBJECT
*object
)
1197 g_return_if_fail (object
!=NULL
);
1198 g_return_if_fail ((object
->type
== OBJ_COMPLEX
) ||
1199 (object
->type
== OBJ_PLACEHOLDER
));
1201 x
= object
->complex->x
+ (-centerx
);
1202 y
= object
->complex->y
+ (-centery
);
1204 rotate_point_90(x
, y
, angle
, &newx
, &newy
);
1206 x
= newx
+ (centerx
);
1207 y
= newy
+ (centery
);
1209 o_complex_translate_world(-object
->complex->x
,
1210 -object
->complex->y
, object
);
1211 o_list_rotate_world(0, 0, angle
, object
->complex->prim_objs
);
1213 object
->complex->x
= 0;
1214 object
->complex->y
= 0;
1216 o_complex_translate_world(x
, y
, object
);
1218 object
->complex->angle
= ( object
->complex->angle
+ angle
) % 360;
1222 /*! \todo Finish function documentation!!!
1224 * \par Function Description
1227 void o_complex_mirror_world(int world_centerx
, int world_centery
,
1232 g_return_if_fail( object
!= NULL
);
1233 g_return_if_fail( (object
->type
== OBJ_COMPLEX
||
1234 object
->type
== OBJ_PLACEHOLDER
) );
1235 g_return_if_fail( object
->complex != NULL
);
1237 x
= 2 * world_centerx
- object
->complex->x
;
1238 y
= object
->complex->y
;
1240 o_complex_translate_world(-object
->complex->x
,
1241 -object
->complex->y
, object
);
1243 o_list_mirror_world(0, 0, object
->complex->prim_objs
);
1245 switch(object
->complex->angle
) {
1247 object
->complex->angle
= 270;
1251 object
->complex->angle
= 90;
1256 object
->complex->mirror
= !object
->complex->mirror
;
1258 o_complex_translate_world(x
, y
, object
);
1261 struct return_pin_context
{
1266 static enum visit_result
return_pin_one(OBJECT
*o_current
, void *userdata
)
1268 struct return_pin_context
*ctx
= userdata
;
1271 switch(o_current
->type
) {
1273 /* Search for the pin making sure that */
1274 /* any found attribute starts with "pinnumber" */
1275 found
= o_attrib_search_attrib_value(o_current
->attribs
, ctx
->pin
,
1279 printf("%s: %s\n", found
->name
, o_text_get_string(found
));
1281 ctx
->found
= o_current
;
1282 return VISIT_RES_EARLY
;
1287 return VISIT_RES_OK
;
1290 /*! \brief search the pin with a given pin number
1291 * \par Function Description
1292 * This function searches a pin object inside the complex \a object.
1293 * The pin name is a character string \a pin.
1295 * \param object The complex object
1296 * \param pin The pin number (string) to find
1297 * \return a pin object if found, NULL otherwise
1299 OBJECT
*o_complex_return_pin_object(OBJECT
*object
, char *pin
)
1301 struct return_pin_context ctx
= {
1307 * It is a semantic error to have multiple pins with the same pinnumber, so
1308 * VISIT_ANY is ok here.
1310 s_visit_object(object
, &return_pin_one
, &ctx
, VISIT_ANY
, 1);
1315 /*! \brief check the symversion of a complex object
1316 * \par Function Description
1317 * This function compares the symversion of a symbol with it's
1318 * earlier saved symversion in a schematic.
1319 * Major symversion changes are added to the toplevel object
1320 * (toplevel->major_changed_refdes), minor changes are reported
1321 * to the messaging system.
1323 * \param toplevel The TOPLEVEL object
1324 * \param object The complex OBJECT
1327 o_complex_check_symversion(TOPLEVEL
*toplevel
, OBJECT
const *object
)
1329 char *inside
= NULL
;
1330 char *outside
= NULL
;
1331 char *refdes
= NULL
;
1332 double inside_value
= -1.0;
1333 double outside_value
= -1.0;
1334 char *err_check
= NULL
;
1335 int inside_present
= FALSE
;
1336 int outside_present
= FALSE
;
1337 double inside_major
, inside_minor
;
1338 double outside_major
, outside_minor
;
1340 g_return_if_fail (object
!= NULL
);
1341 g_return_if_fail ((object
->type
== OBJ_COMPLEX
||
1342 object
->type
== OBJ_PLACEHOLDER
));
1343 g_return_if_fail (object
->complex != NULL
);
1345 /* first look on the inside for the symversion= attribute */
1346 inside
= o_attrib_search_object(object
, "symversion", 0);
1348 /* now look for the symversion= attached to object */
1349 outside
= o_attrib_search_attrib_name(object
->attribs
, "symversion", 0);
1351 /* get the uref for future use */
1352 refdes
= o_attrib_search_attrib_name(object
->attribs
, "refdes", 0);
1355 refdes
= g_strdup ("unknown");
1360 inside_value
= strtod(inside
, &err_check
);
1361 if (inside_value
== 0 && inside
== err_check
)
1365 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1366 "\tCould not parse symbol file symversion=%s\n"),
1369 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1370 "\tCould not parse symbol file symversion=\n"),
1375 inside_present
= TRUE
;
1377 inside_present
= FALSE
; /* attribute not inside */
1382 outside_value
= strtod(outside
, &err_check
);
1383 if (outside_value
== 0 && outside
== err_check
)
1385 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1386 "\tCould not parse attached symversion=%s\n"),
1390 outside_present
= TRUE
;
1392 outside_present
= FALSE
; /* attribute not outside */
1396 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object
->name
,
1397 inside_value
, outside_value
);
1400 /* symversion= is not present anywhere */
1401 if (!inside_present
&& !outside_present
)
1403 /* symbol is legacy and versioned okay */
1407 /* No symversion inside, but a version is outside, this is a weird case */
1408 if (!inside_present
&& outside_present
)
1410 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1411 "\tsymversion=%s attached to instantiated symbol, "
1412 "but no symversion= inside symbol file\n"),
1417 /* inside & not outside is a valid case, means symbol in library is newer */
1418 /* also if inside_value is greater than outside_value, then symbol in */
1419 /* library is newer */
1420 if ((inside_present
&& !outside_present
) ||
1421 ((inside_present
&& outside_present
) && (inside_value
> outside_value
)))
1424 s_log_message(_("WARNING: Symbol version mismatch on refdes %s (%s):\n"
1425 "\tSymbol in library is newer than "
1426 "instantiated symbol\n"),
1427 refdes
, object
->complex_basename
);
1429 /* break up the version values into major.minor numbers */
1430 inside_major
= floor(inside_value
);
1431 inside_minor
= inside_value
- inside_major
;
1433 if (outside_present
)
1435 outside_major
= floor(outside_value
);
1436 outside_minor
= outside_value
- outside_major
;
1438 /* symversion was not attached to the symbol, set all to zero */
1439 outside_major
= 0.0;
1440 outside_minor
= 0.0;
1441 outside_value
= 0.0;
1445 printf("i: %f %f %f\n", inside_value
, inside_major
, inside_minor
);
1446 printf("o: %f %f %f\n", outside_value
, outside_major
, outside_minor
);
1449 if (inside_major
> outside_major
)
1452 s_log_message(_("\tMAJOR VERSION CHANGE (file %.3f, "
1453 "instantiated %.3f, %s)!\n"),
1454 inside_value
, outside_value
, refdes
);
1456 /* add the refdes to the major_changed_refdes GList */
1457 /* make sure refdes_copy is freed somewhere */
1458 refdes_copy
= g_strconcat (refdes
, " (",
1459 object
->complex_basename
,
1461 toplevel
->major_changed_refdes
=
1462 g_list_append(toplevel
->major_changed_refdes
, refdes_copy
);
1464 /* don't bother checking minor changes if there are major ones*/
1468 if (inside_minor
> outside_minor
)
1470 s_log_message(_("\tMinor version change (file %.3f, "
1471 "instantiated %.3f)\n"),
1472 inside_value
, outside_value
);
1478 /* outside value is greater than inside value, this is weird case */
1479 if ((inside_present
&& outside_present
) && (outside_value
> inside_value
))
1481 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1482 "\tInstantiated symbol is newer than "
1483 "symbol in library\n"),
1488 /* if inside_value and outside_value match, then symbol versions are okay */
1496 /*! \brief Calculates the distance between the given point and the closest
1497 * point on an object within the complex object.
1499 * \param [in] complex The complex of the OBJECT
1500 * \param [in] x The x coordinate of the given point.
1501 * \param [in] y The y coordinate of the given point.
1502 * \return The shortest distance from the object to the point. If the
1503 * distance cannot be calculated, this function returns a really large
1504 * number (G_MAXDOUBLE). With an invalid parameter, this function returns
1507 gdouble
o_complex_shortest_distance(OBJECT
*o_current
, gint x
, gint y
)
1510 gdouble shortest_distance
= G_MAXDOUBLE
;
1513 g_return_val_if_fail(o_current
->complex, G_MAXDOUBLE
);
1515 temp
= o_current
->complex->prim_objs
;
1521 while (temp
!= NULL
) {
1522 distance
= o_shortest_distance(temp
, x
, y
);
1524 shortest_distance
= min(shortest_distance
, distance
);
1529 return shortest_distance
;