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 void o_complex_recalc(OBJECT
*o_current
);
46 static void o_complex_print(TOPLEVEL
*toplevel
, FILE *fp
, OBJECT
*o_current
,
47 double scale
, GArray
*unicode_table
);
48 static void o_complex_grip_foreach(OBJECT
*o
,
49 gboolean (*fn
)(OBJECT
*o
,
50 int grip_x
, int grip_y
,
54 static void o_complex_embed(TOPLEVEL
*toplevel
, OBJECT
*o
, char yes
);
55 static enum visit_result
56 o_complex_visit(OBJECT
*o_current
,
57 enum visit_result (*fn
)(OBJECT
*, void *), void *context
,
58 enum visit_order order
, int depth
);
59 static OBJECT
*o_complex_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
);
60 static OBJECT
*o_complex_copy_embedded(TOPLEVEL
*toplevel
, OBJECT
*o_current
);
62 /*! Default setting for complex draw function. */
63 void (*complex_draw_func
)() = NULL
;
65 /*! \brief Return the bounds of the given object.
66 * \par Given an object, calculate the bounds coordinates.
67 * \param [in] toplevel The toplevel structure.
68 * \param [in] o_current The object to look the bounds for.
69 * \param [out] rleft pointer to the left coordinate of the object.
70 * \param [out] rtop pointer to the top coordinate of the object.
71 * \param [out] rright pointer to the right coordinate of the object.
72 * \param [out] rbottom pointer to the bottom coordinate of the object.
73 * \return If any bounds were found for the object
74 * \retval 0 No bound was found
75 * \retval 1 Bound was found
77 int world_get_single_object_bounds(OBJECT
*o_current
,
78 int *rleft
, int *rtop
, int *rright
, int *rbottom
)
80 g_return_val_if_fail(o_current
, 0);
82 if (!o_current
->w_bounds_valid
) {
83 o_recalc_single_object(o_current
);
84 if (!o_current
->w_bounds_valid
) {
88 *rleft
= o_current
->w_left
;
89 *rtop
= o_current
->w_top
;
90 *rright
= o_current
->w_right
;
91 *rbottom
= o_current
->w_bottom
;
95 struct get_bounds_context
{
97 int *left
, *top
, *right
, *bottom
;
100 static enum visit_result
get_bounds_one(OBJECT
*o_current
, void *userdata
)
102 struct get_bounds_context
*ctx
= userdata
;
103 int rleft
, rtop
, rright
, rbottom
;
105 if (world_get_single_object_bounds(o_current
, &rleft
, &rtop
, &rright
, &rbottom
)) {
107 *ctx
->left
= min(*ctx
->left
, rleft
);
108 *ctx
->top
= min(*ctx
->top
, rtop
);
109 *ctx
->right
= max(*ctx
->right
, rright
);
110 *ctx
->bottom
= max(*ctx
->bottom
, rbottom
);
114 *ctx
->right
= rright
;
115 *ctx
->bottom
= rbottom
;
123 /*! \brief Return the bounds of the given list of objects.
124 * \par Given a list of objects, calculate the bounds coordinates.
125 * \param [in] complex The list of objects to look the bounds for.
126 * \param [out] left pointer to the left coordinate of the object.
127 * \param [out] top pointer to the top coordinate of the object.
128 * \param [out] right pointer to the right coordinate of the object.
129 * \param [out] bottom pointer to the bottom coordinate of the object.
130 * \return If any bounds were found for the list of objects
131 * \retval 0 No bounds were found
132 * \retval 1 Bound was found
135 world_get_object_list_bounds(OBJECT
*complex, enum list_kind kind
,
136 int *left
, int *top
, int *right
, int *bottom
)
138 struct get_bounds_context context
= {
146 s_visit_list(complex, kind
, &get_bounds_one
, &context
, VISIT_ANY
, 1);
148 return context
.found
;
151 /*! \brief Return the bounds of the given GList of objects.
152 * \par Given a list of objects, calculate the bounds coordinates.
153 * \param [in] head The list of objects to look the bounds for.
154 * \param [out] left pointer to the left coordinate of the object.
155 * \param [out] top pointer to the top coordinate of the object.
156 * \param [out] right pointer to the right coordinate of the object.
157 * \param [out] bottom pointer to the bottom coordinate of the object.
158 * \return If any bounds were found for the list of objects
159 * \retval 0 No bounds were found
160 * \retval 1 Bound was found
162 int world_get_object_glist_bounds(GList
const *head
,
163 int *left
, int *top
, int *right
, int *bottom
)
165 GList
const *s_current
=NULL
;
166 OBJECT
*o_current
=NULL
;
167 int rleft
, rtop
, rright
, rbottom
;
172 /* Find the first object with bounds, and set the bounds variables, then expand as necessary. */
173 while ( s_current
!= NULL
) {
174 o_current
= s_current
->data
;
175 g_assert (o_current
!= NULL
);
176 if (world_get_single_object_bounds(o_current
, &rleft
, &rtop
, &rright
, &rbottom
)) {
178 *left
= min( *left
, rleft
);
179 *top
= min( *top
, rtop
);
180 *right
= max( *right
, rright
);
181 *bottom
= max( *bottom
, rbottom
);
190 s_current
= g_list_next (s_current
);
195 /*! \brief Queries the bounds of a complex object.
196 * \par Function Description
197 * This function returns the bounding box of the complex object
200 * \param [in] complex The complex object.
201 * \param [out] left The leftmost edge of the bounding box (in
203 * \param [out] top The upper edge of the bounding box (in
205 * \param [out] right The rightmost edge of the bounding box (in
207 * \param [out] bottom The bottom edge of the bounding box (in
210 void world_get_complex_bounds(OBJECT
*complex,
211 int *left
, int *top
, int *right
, int *bottom
)
213 g_return_if_fail (complex != NULL
&&
214 (complex->type
== OBJ_COMPLEX
||
215 complex->type
== OBJ_PLACEHOLDER
) &&
216 complex->complex != NULL
);
218 world_get_object_list_bounds(complex->complex->prim_objs
, LIST_KIND_HEAD
,
219 left
, top
, right
, bottom
);
222 /*! \brief create a new head object
223 * \par Function Description
224 * This function creates a <b>complex_head</b> OBJECT. This OBJECT
225 * is just a special empty object. This object is never modified.
227 * \return new head OBJECT
229 OBJECT
*new_head(TOPLEVEL
*toplevel
)
231 OBJECT
*new_node
=NULL
;
233 new_node
= s_toplevel_new_object(toplevel
, OBJ_HEAD
, "complex_head");
238 char *o_complex_get_refdes(OBJECT
const *o_current
,
239 char const *default_value
)
241 static GHashTable
*objects_warned
= NULL
;
244 /* Casting away const is okay because we don't use return_objects. */
245 refdes
= o_attrib_search_name_single((OBJECT
*) o_current
, "refdes", NULL
);
246 if (refdes
== NULL
) {
247 refdes
= o_attrib_search_name_single((OBJECT
*) o_current
, "uref", NULL
);
249 /* Warn once about a symbol using uref instead of refdes. */
250 if (!objects_warned
) {
251 objects_warned
= g_hash_table_new(&g_direct_hash
, &g_direct_equal
);
252 libgeda_caches
= g_list_prepend(libgeda_caches
, objects_warned
);
254 if (!g_hash_table_lookup_extended(objects_warned
,
255 GINT_TO_POINTER(o_current
->sid
),
257 g_warning(_("Found uref=%s; please use refdes=\n"), refdes
);
258 g_hash_table_replace(objects_warned
,
259 GINT_TO_POINTER(o_current
->sid
),
260 GINT_TO_POINTER(o_current
->sid
));
263 refdes
= default_value
? g_strdup(default_value
) : NULL
;
270 /*! \brief check whether an object is a attributes
271 * \par Function Description
272 * This function checks if an object should be promoted.
273 * An attribute object is promotable if it's promoted by default, or the user
274 * has configured it to promote an attribute.
276 * \param [in] toplevel The TOPLEVEL object
277 * \param [in] object The attribute object to check
278 * \return TRUE if the object is a eligible attribute, FALSE otherwise
280 static int o_complex_is_eligible_attribute (TOPLEVEL
*toplevel
, OBJECT
*object
)
283 int promotableAttribute
= FALSE
;
285 g_return_val_if_fail(object
->type
== OBJ_TEXT
, FALSE
);
287 /* check list against attributes which can be promoted */
288 if (toplevel
->always_promote_attributes
!= NULL
) {
289 if (o_attrib_get_name_value(o_text_get_string(object
), &name
, NULL
)) {
290 if (g_list_find_custom(toplevel
->always_promote_attributes
,
291 name
, (GCompareFunc
) strcmp
) != NULL
) {
292 /* Name of the attribute was in the always promote attributes list */
293 promotableAttribute
= TRUE
;
297 if (promotableAttribute
)
302 /* object is invisible and we do not want to promote invisible text */
303 if (object
->visibility
== INVISIBLE
&& toplevel
->promote_invisible
== FALSE
)
304 return FALSE
; /* attribute not eligible for promotion */
306 /* yup, attribute can be promoted */
310 /*! \brief get the embedded state of an complex object
311 * \par Function Description
312 * Checks and returns the status of the complex object.
314 * \param o_current The object to check
315 * \return 1 if embedded, 0 otherwise
317 int o_complex_is_embedded(OBJECT
const *o_current
)
319 g_return_val_if_fail(o_current
!= NULL
, 0);
321 if(o_current
->complex == NULL
)
324 if (o_current
->complex->is_embedded
) {
331 static enum visit_result
get_toplevel_attribs_one(OBJECT
*o_current
,
334 GList
**o_list
= userdata
;
336 if (o_current
->type
== OBJ_TEXT
&&
337 o_current
->attached_to
== NULL
&&
338 o_attrib_get_name_value(o_text_get_string(o_current
), NULL
, NULL
)) {
339 *o_list
= g_list_prepend(*o_list
, o_current
);
345 /*! \brief Get a list of all toplevel attributes of and object list
347 * \par Function Description
348 * Returns a GList of all attribute OBJECTs
350 * \param [in] o_head The head of the object list
351 * \returns A GList of attribute OBJECTs
353 GList
*o_complex_get_toplevel_attribs(OBJECT
*o_head
)
355 GList
*o_list
= NULL
;
357 s_visit_list(o_head
, LIST_KIND_HEAD
, &get_toplevel_attribs_one
, &o_list
,
359 o_list
= g_list_reverse (o_list
);
365 /*! \brief Get attributes eligible for promotion from inside a complex
367 * \par Function Description
368 * Returns a GList of OBJECTs which are eligible for promotion from
369 * within the passed complex OBJECT.
371 * If detach is TRUE, the function removes these attribute objects from
372 * the prim_objs of the complex. It detached, the returned OBJECTs are
373 * isolated from each other, having their next and prev pointers set to NULL.
375 * If detach is FALSE, the OBJECTs are left in place. Their next and prev
376 * pointers form part of the complex's prim_objs linked list.
378 * \param [in] toplevel The toplevel environment.
379 * \param [in] object The complex object being modified.
380 * \param [in] detach Should the attributes be detached?
381 * \returns A linked list of OBJECTs to promote.
383 GList
*o_complex_get_promotable (TOPLEVEL
*toplevel
, OBJECT
*object
, int detach
)
385 GList
*promoted
= NULL
;
390 if (!toplevel
->attribute_promotion
) /* controlled through rc file */
393 attribs
= o_complex_get_toplevel_attribs(object
->complex->prim_objs
);
395 for (iter
= attribs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
398 /* Is it an attribute we want to promote? */
399 if (!o_complex_is_eligible_attribute(toplevel
, tmp
))
403 /* Remove and isolate it from the complex list */
404 s_basic_unlink_object(tmp
);
405 tmp
->complex_parent
= NULL
;
408 promoted
= g_list_prepend (promoted
, tmp
);
411 g_list_free (attribs
);
413 promoted
= g_list_reverse (promoted
);
418 /*! \brief Promote attributes from the passed OBJECT
420 * \par Function Description
421 * Promotes attributes from the passed OBJECT, linking them into the
422 * OBJECT linked list immediately prior to the passed OBJECT.
424 * \param [in] toplevel The toplevel environment.
425 * \param [in] page The PAGE on which \a object exists.
426 * \param [in] object The complex object whose attributes are being promoted.
428 void o_complex_promote_attribs(TOPLEVEL
*toplevel
, PAGE
*page
, OBJECT
*object
)
431 OBJECT
*first_promoted
, *last_promoted
;
434 promoted
= o_complex_get_promotable (toplevel
, object
, TRUE
);
436 if (promoted
== NULL
)
439 /* Link the promoted OBJECTs together */
440 o_glist_relink_objects (promoted
);
442 first_promoted
= promoted
->data
;
443 last_promoted
= g_list_last (promoted
)->data
;
445 /* Insert promoted attributes before the complex in the object list */
446 s_basic_splice(object
, first_promoted
, last_promoted
);
448 for (o
= first_promoted
; o
!= last_promoted
; o
= o
->next
) {
449 /* XXX Emit signal only after the objects are on the page. */
450 g_signal_emit_by_name(page
, "add-object", o
);
454 /* Attach promoted attributes to the original complex object */
455 o_attrib_attach_list (toplevel
, promoted
, object
);
457 g_list_free (promoted
);
461 /*! \brief Delete or hide promotable from the passed OBJECT
463 * \par Function Description
464 * Deletes or hides promotable attributes from the passed OBJECT.
465 * This is used when loading symbols during the load of a schematic from
466 * disk. The schematic will already contain local copies of symbol's
467 * promotable objects, so we delete or hide the symbol's copies.
469 * Deletion / hiding is dependant on the setting of
470 * toplevel->keep_invisible. If true, attributes eligible for
471 * promotion are kept in memory but flagged as invisible.
473 * \param [in] toplevel The toplevel environment.
474 * \param [in] object The complex object being altered.
476 void o_complex_remove_promotable_attribs (TOPLEVEL
*toplevel
, OBJECT
*object
)
478 GList
*promotable
, *iter
;
480 promotable
= o_complex_get_promotable (toplevel
, object
, FALSE
);
482 if (promotable
== NULL
)
485 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
486 OBJECT
*a_object
= iter
->data
;
487 g_warn_if_fail(a_object
->type
== OBJ_TEXT
);
488 if (toplevel
->keep_invisible
== TRUE
) {
489 /* Hide promotable attributes */
490 o_text_change(a_object
, NULL
, INVISIBLE
, LEAVE_NAME_VALUE_ALONE
);
492 s_delete (toplevel
, a_object
); /* Delete promotable attributes */
496 g_list_free (promotable
);
499 static void o_complex_notice_destroy(OBJECT
*subject
, void *userdata
)
501 TOPLEVEL
*toplevel
= userdata
;
503 /* Evict all slotted symbols. */
504 s_slot_destroy_complex(toplevel
, subject
);
506 /* Detach from any owning slot. */
507 s_slot_unlink(subject
);
512 * \par Function Description
515 OBJECT
*o_complex_new(TOPLEVEL
*toplevel
,
517 int color
, int x
, int y
, int angle
,
518 int mirror
, const CLibSymbol
*clib
,
519 const gchar
*basename
,
522 OBJECT
*new_node
=NULL
;
523 OBJECT
*prim_objs
=NULL
;
524 OBJECT
*new_prim_obj
;
526 int save_adding_sel
= 0;
527 int loaded_normally
= FALSE
;
531 new_node
= s_toplevel_new_object(toplevel
, type
, "complex");
532 g_signal_connect(G_OBJECT(new_node
), "remove",
533 G_CALLBACK(o_complex_notice_destroy
), toplevel
);
536 new_node
->complex_basename
= g_strdup (s_clib_symbol_get_name (clib
));
538 new_node
->complex_basename
= g_strdup (basename
);
541 new_node
->color
= color
;
542 new_node
->selectable
= selectable
;
544 new_node
->complex = g_malloc(sizeof (COMPLEX
));
546 new_node
->complex->angle
= angle
;
547 new_node
->complex->mirror
= mirror
;
548 new_node
->complex->is_embedded
= FALSE
;
550 new_node
->complex->x
= x
;
551 new_node
->complex->y
= y
;
553 new_node
->copy_func
= &o_complex_copy
;
554 new_node
->bounds_recalc_func
= o_complex_recalc
;
555 new_node
->draw_func
= complex_draw_func
;
556 new_node
->psprint_func
= &o_complex_print
;
557 new_node
->grip_foreach_func
= &o_complex_grip_foreach
;
558 new_node
->embed_func
= &o_complex_embed
;
559 new_node
->visit_func
= &o_complex_visit
;
561 /* this was at the beginning and p_complex was = to complex */
562 prim_objs
= new_head(toplevel
);
564 /* get the symbol data */
566 buffer
= s_clib_symbol_get_data (clib
);
569 save_adding_sel
= toplevel
->ADDING_SEL
;
570 toplevel
->ADDING_SEL
= 1; /* name is hack, don't want to */
572 if (clib
== NULL
|| buffer
== NULL
) {
573 OBJECT
*save_prim_objs
;
574 char *not_found_text
= NULL
;
575 int left
, right
, top
, bottom
;
576 int x_offset
, y_offset
;
578 /* filename was NOT found */
579 loaded_normally
= FALSE
;
581 /* save the prim_objs pointer, since the following code modifies it */
582 save_prim_objs
= prim_objs
;
584 /* Put placeholder into object list. Changed by SDB on
585 * 1.19.2005 to fix problem that symbols were silently
586 * deleted by gattrib when RC files were messed up. */
587 new_node
->type
= OBJ_PLACEHOLDER
;
589 /* Mark the origin of the missing component */
590 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
591 DETACHED_ATTRIBUTE_COLOR
,
592 x
- 50, y
, x
+ 50, y
);
593 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
594 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
595 DETACHED_ATTRIBUTE_COLOR
,
596 x
, y
+ 50, x
, y
- 50);
597 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
599 /* Add some useful text */
601 g_strdup_printf (_("Component not found:\n %s"),
602 new_node
->complex_basename
);
603 new_prim_obj
= o_text_new(toplevel
,
604 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
605 x
+ NOT_FOUND_TEXT_X
,
606 y
+ NOT_FOUND_TEXT_Y
, LOWER_LEFT
, 0,
608 VISIBLE
, SHOW_NAME_VALUE
);
609 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
610 g_free(not_found_text
);
612 /* figure out where to put the hazard triangle */
613 world_get_text_bounds(prim_objs
, &left
, &top
, &right
, &bottom
);
614 x_offset
= (right
- left
) / 4;
615 y_offset
= bottom
- top
+ 100; /* 100 is just an additional offset */
617 /* TODO: set buffer to string constant that defines the triangle. */
618 /* add hazard triangle */
619 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
620 DETACHED_ATTRIBUTE_COLOR
,
621 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
622 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
623 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
624 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
625 o_set_line_options(new_prim_obj
, END_ROUND
, TYPE_SOLID
, 50, -1, -1);
626 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
627 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
628 DETACHED_ATTRIBUTE_COLOR
,
629 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
630 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
631 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
632 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500);
633 o_set_line_options(new_prim_obj
, END_ROUND
, TYPE_SOLID
, 50, -1, -1);
634 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
635 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
636 DETACHED_ATTRIBUTE_COLOR
,
637 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
638 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500,
639 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
640 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
641 o_set_line_options(new_prim_obj
, END_ROUND
, TYPE_SOLID
, 50, -1, -1);
642 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
643 new_prim_obj
= o_text_new(toplevel
,
644 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
645 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 270,
646 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 90,
647 LOWER_LEFT
, 0, "!", 18,
648 VISIBLE
, SHOW_NAME_VALUE
);
649 prim_objs
= s_basic_link_object(new_prim_obj
, prim_objs
);
650 prim_objs
= save_prim_objs
;
652 /* filename was found */
653 loaded_normally
= TRUE
;
655 /* add connections till translated */
656 o_read_buffer(toplevel
, prim_objs
, buffer
, -1, new_node
->complex_basename
);
660 toplevel
->ADDING_SEL
= save_adding_sel
;
662 /* do not mirror/rotate/translate/connect the primitive objects if the
663 * component was not loaded via o_read
665 if (loaded_normally
== TRUE
) {
667 o_list_mirror_world(0, 0, prim_objs
);
670 o_list_rotate_world(0, 0, angle
, prim_objs
);
671 o_list_translate_world(x
, y
, prim_objs
);
674 new_node
->complex->prim_objs
= prim_objs
;
676 /* set the parent field now */
677 for (tmp
= prim_objs
; tmp
!= NULL
; tmp
= tmp
->next
) {
678 tmp
->complex_parent
= new_node
;
681 o_complex_recalc(new_node
);
683 s_slot_setup_complex(toplevel
, new_node
);
688 /*! \brief create a new embedded object
689 * \par Function Description
690 * This function creates a new embedded object.
692 * \param [in] toplevel The TOPLEVEL object
693 * \param [in] type The type of the object (usually OBJ_COMLEX)
694 * \param [in] color The color of the object
695 * \param [in] x The x location of the complex object
696 * \param [in] y The y location of the complex object
697 * \param [in] angle The rotation angle
698 * \param [in] mirror The mirror status
699 * \param [in] basename The basic name the embedded was created of
700 * \param [in] selectable whether the object can be selected with the mouse
701 * \return a new complex object
703 OBJECT
*o_complex_new_embedded(TOPLEVEL
*toplevel
,
704 char type
, int color
, int x
, int y
, int angle
, int mirror
,
705 const gchar
*basename
, int selectable
)
707 OBJECT
*prim_objs
=NULL
;
708 OBJECT
*new_node
=NULL
;
711 new_node
= s_toplevel_new_object(toplevel
, type
, "complex");
713 new_node
->complex = g_malloc(sizeof (COMPLEX
));
714 new_node
->complex->x
= x
;
715 new_node
->complex->y
= y
;
717 new_node
->complex->angle
= angle
;
718 new_node
->complex->mirror
= mirror
;
719 new_node
->complex->is_embedded
= TRUE
;
721 new_node
->complex_basename
= g_strdup(basename
);
723 new_node
->color
= color
;
724 new_node
->selectable
= selectable
;
726 new_node
->copy_func
= &o_complex_copy_embedded
;
727 new_node
->bounds_recalc_func
= o_complex_recalc
;
728 new_node
->draw_func
= complex_draw_func
;
729 new_node
->psprint_func
= &o_complex_print
;
730 new_node
->grip_foreach_func
= &o_complex_grip_foreach
;
731 new_node
->embed_func
= &o_complex_embed
;
732 new_node
->visit_func
= &o_complex_visit
;
734 /* this was at the beginning and p_complex was = to complex */
735 prim_objs
= new_head(toplevel
);
736 new_node
->complex->prim_objs
= prim_objs
;
738 /* set the parent field now */
739 /* XXX This is ineffective, since we don't have prim_objs yet! */
740 for (tmp
= prim_objs
; tmp
!= NULL
; tmp
= tmp
->next
) {
741 tmp
->complex_parent
= new_node
;
744 /* don't have to translate/rotate/mirror here at all since the */
745 /* object is in place */
749 /*! \brief update the visual boundaries of the complex object
750 * \par Function Description
751 * This function updates the boundaries of the object \a o_current.
753 * \param [in] toplevel The TOPLEVEL object
754 * \param [in] o_current The OBJECT to update
756 static void o_complex_recalc(OBJECT
*o_current
)
758 int left
, right
, top
, bottom
;
760 /* recalc routine Add this somewhere */
762 /* o_recalc(o_current->complex);*/
764 if ((!o_current
) || (o_current
->type
!= OBJ_COMPLEX
&& o_current
->type
!= OBJ_PLACEHOLDER
))
767 if (o_current
->complex->x
== -1 && o_current
->complex->y
== -1) {
768 /* Off-schematic components have no bounding box. */
772 world_get_complex_bounds(o_current
, &left
, &top
, &right
, &bottom
);
773 o_current
->w_left
= left
;
774 o_current
->w_top
= top
;
775 o_current
->w_right
= right
;
776 o_current
->w_bottom
= bottom
;
777 o_current
->w_bounds_valid
= TRUE
;
780 /*! \brief read a complex object from a char buffer
781 * \par Function Description
782 * This function reads a complex object from the buffer \a buf.
784 * \param [in] toplevel The TOPLEVEL object
785 * \param [in] buf a text buffer (usually a line of a schematic file)
786 * \param [in] release_ver The release number gEDA
787 * \param [in] fileformat_ver a integer value of the file format
788 * \return The new object
790 * \todo Don't use fixed-length string for symbol basename
792 OBJECT
*o_complex_read(TOPLEVEL
*toplevel
,
793 char buf
[], unsigned int release_ver
,
794 unsigned int fileformat_ver
)
801 char basename
[256]; /* FIXME This is a hack */
806 sscanf(buf
, "%c %d %d %d %d %d %s\n",
807 &type
, &x1
, &y1
, &selectable
, &angle
, &mirror
, basename
);
817 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
);
827 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
);
830 if (strncmp(basename
, "EMBEDDED", 8) == 0) {
831 new_obj
= o_complex_new_embedded(toplevel
, type
,
832 WHITE
, x1
, y1
, angle
, mirror
,
836 const CLibSymbol
*clib
= s_clib_get_symbol_by_name (basename
);
838 new_obj
= o_complex_new(toplevel
, type
,
842 basename
, selectable
);
843 /* Delete or hide attributes eligible for promotion inside the complex */
844 o_complex_remove_promotable_attribs (toplevel
, new_obj
);
850 /*! \brief Create a string representation of the complex object
851 * \par Function Description
852 * This function takes a complex \a object and return a string
853 * according to the file format definition.
855 * \param [in] object a complex OBJECT
856 * \return the string representation of the complex OBJECT
858 char *o_complex_save(OBJECT
*object
)
863 g_return_val_if_fail (object
!= NULL
, NULL
);
865 if ((object
->type
== OBJ_COMPLEX
) || (object
->type
== OBJ_PLACEHOLDER
)) {
866 basename
= g_strdup_printf ("%s%s",
867 object
->complex->is_embedded
? "EMBEDDED" : "",
868 object
->complex_basename
);
869 /* We force the object type to be output as OBJ_COMPLEX for both
870 * these object types. */
871 buf
= g_strdup_printf("%c %d %d %d %d %d %s", OBJ_COMPLEX
,
872 object
->complex->x
, object
->complex->y
,
873 object
->selectable
, object
->complex->angle
,
874 object
->complex->mirror
, basename
);
881 /*! \brief move a complex object
882 * \par Function Description
883 * This function changes the position of a complex \a object.
885 * \param [in] dx The x-distance to move the object
886 * \param [in] dy The y-distance to move the object
887 * \param [in] object The complex OBJECT to be moved
889 void o_complex_translate_world(int dx
, int dy
, OBJECT
*object
)
891 g_return_if_fail (object
!= NULL
&&
892 (object
->type
== OBJ_COMPLEX
||
893 object
->type
== OBJ_PLACEHOLDER
));
895 object
->complex->x
= object
->complex->x
+ dx
;
896 object
->complex->y
= object
->complex->y
+ dy
;
898 o_list_translate_world(dx
, dy
, object
->complex->prim_objs
);
900 o_complex_recalc(object
);
903 static void o_complex_print(TOPLEVEL
*toplevel
, FILE *fp
, OBJECT
*o_current
,
904 double scale
, GArray
*unicode_table
)
906 fprintf(fp
, "gsave\n");
907 f_print_objects(toplevel
, fp
,
908 o_current
->complex->prim_objs
,
911 fprintf(fp
, "grestore\n");
914 static void o_complex_grip_foreach(OBJECT
*o
,
915 gboolean (*fn
)(OBJECT
*o
,
916 int grip_x
, int grip_y
,
917 enum grip_t whichone
,
921 const GRIP complex_grips
[] = {
925 .whichone
= GRIP_COMPLEX_ORIGIN
,
927 { .whichone
= GRIP_NONE
}
930 s_basic_grip_foreach_helper(o
, complex_grips
, fn
, userdata
);
933 static void o_complex_embed(TOPLEVEL
*toplevel
, OBJECT
*o
, char yes
)
936 g_assert(o
->type
== OBJ_COMPLEX
|| o
->type
== OBJ_PLACEHOLDER
);
938 if (!o_complex_is_embedded(o
) == !yes
) {
943 /* Set the embedded flag. */
944 o
->complex->is_embedded
= TRUE
;
945 o
->copy_func
= &o_complex_copy_embedded
;
947 s_log_message("Component [%s] has been embedded\n",
948 o
->complex_basename
);
950 const CLibSymbol
*sym
;
952 /* search for the symbol in the library */
953 sym
= s_clib_get_symbol_by_name(o
->complex_basename
);
956 /* symbol not found in the symbol library: signal an error */
957 s_log_message(_("Could not find component [%s], while trying to "
958 "unembed. Component is still embedded\n"),
959 o
->complex_basename
);
963 /* clear the embedded flag */
964 o
->complex->is_embedded
= FALSE
;
965 o
->copy_func
= &o_complex_copy
;
967 s_log_message(_("Component [%s] has been successfully unembedded\n"),
968 o
->complex_basename
);
971 toplevel
->page_current
->CHANGED
= 1; /* FIXME: Do this from an observer. */
974 static enum visit_result
975 o_complex_visit(OBJECT
*o_current
,
976 enum visit_result (*fn
)(OBJECT
*, void *), void *context
,
977 enum visit_order order
, int depth
)
979 g_return_if_fail(o_current
->type
== OBJ_COMPLEX
);
980 g_return_if_fail(o_current
->complex->prim_objs
!= NULL
);
982 return (s_visit_list(o_current
->complex->prim_objs
, LIST_KIND_HEAD
, fn
,
983 context
, order
, depth
));
986 /*! \brief create a copy of a complex object
987 * \par Function Description
988 * This function creates a copy of the complex object \a o_current.
990 * \param [in] toplevel The TOPLEVEL object
991 * \param [in] o_current The object that is copied
992 * \return a new complex object
994 static OBJECT
*o_complex_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
996 OBJECT
*new_obj
=NULL
;
998 const CLibSymbol
*clib
= NULL
;
1000 g_return_val_if_fail(o_current
!= NULL
, NULL
);
1002 if (o_current
->saved_color
== -1) {
1003 color
= o_current
->color
;
1005 color
= o_current
->saved_color
;
1009 * FIXME If the component library path changes at runtime, then a copy
1010 * might not be an identical one. Maybe rather copy prim_objs directly?
1012 clib
= s_clib_get_symbol_by_name (o_current
->complex_basename
);
1014 new_obj
= o_complex_new (toplevel
, o_current
->type
, color
,
1015 o_current
->complex->x
, o_current
->complex->y
,
1016 o_current
->complex->angle
,
1017 o_current
->complex->mirror
,
1018 clib
, o_current
->complex_basename
,
1019 o_current
->selectable
);
1021 /* Delete or hide attributes eligible for promotion inside the complex */
1022 o_complex_remove_promotable_attribs (toplevel
, new_obj
);
1024 o_attrib_slot_copy(toplevel
, o_current
, new_obj
);
1026 /* deal with stuff that has changed */
1028 /* here you need to create a list of attributes which need to be
1029 * connected to the new list, probably make an attribute list and
1030 * fill it with sid's of the attributes */
1035 /*! \brief create a copy of a embedded complex object
1036 * \par Function Description
1037 * This function creates a copy of an embedded complex object \a o_current.
1039 * \param [in] toplevel The TOPLEVEL object
1040 * \param [in] o_current The object that is copied
1041 * \return a new complex object
1043 static OBJECT
*o_complex_copy_embedded(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
1045 OBJECT
*new_obj
=NULL
;
1050 g_return_val_if_fail(o_current
!= NULL
, NULL
);
1052 if (o_current
->saved_color
== -1) {
1053 color
= o_current
->color
;
1055 color
= o_current
->saved_color
;
1058 new_obj
= o_complex_new_embedded (toplevel
, o_current
->type
, color
,
1059 o_current
->complex->x
, o_current
->complex->y
,
1060 o_current
->complex->angle
,
1061 o_current
->complex->mirror
,
1062 o_current
->complex_basename
,
1063 o_current
->selectable
);
1065 /* deal with stuff that has changed */
1067 temp_list
= o_list_copy_all(toplevel
,
1068 o_current
->complex->prim_objs
,
1069 new_obj
->complex->prim_objs
,
1070 toplevel
->ADDING_SEL
);
1072 new_obj
->complex->prim_objs
= return_head(temp_list
);
1074 /* set the parent field now */
1075 for (tmp
= new_obj
->complex->prim_objs
; tmp
!= NULL
; tmp
= tmp
->next
) {
1076 tmp
->complex_parent
= new_obj
;
1079 o_complex_recalc(new_obj
);
1081 /* here you need to create a list of attributes which need to be
1082 * connected to the new list, probably make an attribute list and
1083 * fill it with sid's of the attributes */
1088 static enum visit_result
set_color_one(OBJECT
*o_current
, void *userdata
)
1090 int *color
= userdata
;
1092 o_current
->color
= *color
;
1094 return VISIT_RES_OK
;
1097 /*! \brief Change the color of an object
1098 * \par Function Description
1099 * This function changes the the color of an object
1101 * \param [in] object The object
1102 * \param [in] color The new color
1104 * \note This function is mainly used to change the color of text objects
1106 void o_complex_set_color(OBJECT
*object
, int color
)
1108 s_visit(object
, &set_color_one
, &color
, VISIT_ANY
, VISIT_DEPTH_FOREVER
);
1111 static enum visit_result
set_color_save_one(OBJECT
*o_current
, void *userdata
)
1113 int const *color
= userdata
;
1115 o_current
->saved_color
= o_current
->color
;
1116 o_current
->color
= *color
;
1118 return VISIT_RES_OK
;
1121 /*! \todo Finish function documentation!!!
1123 * \par Function Description
1126 void o_complex_set_color_save(OBJECT
*o_current
, int color
)
1128 s_visit(o_current
, &set_color_save_one
, &color
, VISIT_ANY
,
1129 VISIT_DEPTH_FOREVER
);
1132 static enum visit_result
unset_color_one(OBJECT
*o_current
, void *userdata
)
1134 o_current
->color
= o_current
->saved_color
;
1135 o_current
->saved_color
= -1;
1137 return VISIT_RES_OK
;
1140 /*! \todo Finish function documentation!!!
1142 * \par Function Description
1145 void o_complex_unset_color(OBJECT
*o_current
)
1147 s_visit(o_current
, &unset_color_one
, NULL
, VISIT_ANY
, VISIT_DEPTH_FOREVER
);
1150 static enum visit_result
set_color_save_only_one(OBJECT
*o_current
,
1153 int const *color
= userdata
;
1155 o_current
->saved_color
= *color
;
1157 return VISIT_RES_OK
;
1160 /*! \todo Finish function documentation!!!
1162 * \par Function Description
1165 void o_complex_set_saved_color_only(OBJECT
*o_current
, int color
)
1167 s_visit(o_current
, &set_color_save_only_one
, &color
, VISIT_ANY
,
1168 VISIT_DEPTH_FOREVER
);
1171 /*! \todo Finish function documentation!!!
1173 * \par Function Description
1176 void o_complex_rotate_world(int centerx
, int centery
,
1177 int angle
, OBJECT
*object
)
1182 g_return_if_fail (object
!=NULL
);
1183 g_return_if_fail ((object
->type
== OBJ_COMPLEX
) ||
1184 (object
->type
== OBJ_PLACEHOLDER
));
1186 x
= object
->complex->x
+ (-centerx
);
1187 y
= object
->complex->y
+ (-centery
);
1189 rotate_point_90(x
, y
, angle
, &newx
, &newy
);
1191 x
= newx
+ (centerx
);
1192 y
= newy
+ (centery
);
1194 o_complex_translate_world(-object
->complex->x
,
1195 -object
->complex->y
, object
);
1196 o_list_rotate_world(0, 0, angle
, object
->complex->prim_objs
);
1198 object
->complex->x
= 0;
1199 object
->complex->y
= 0;
1201 o_complex_translate_world(x
, y
, object
);
1203 object
->complex->angle
= ( object
->complex->angle
+ angle
) % 360;
1207 /*! \todo Finish function documentation!!!
1209 * \par Function Description
1212 void o_complex_mirror_world(int world_centerx
, int world_centery
,
1217 g_return_if_fail( object
!= NULL
);
1218 g_return_if_fail( (object
->type
== OBJ_COMPLEX
||
1219 object
->type
== OBJ_PLACEHOLDER
) );
1220 g_return_if_fail( object
->complex != NULL
);
1222 x
= 2 * world_centerx
- object
->complex->x
;
1223 y
= object
->complex->y
;
1225 o_complex_translate_world(-object
->complex->x
,
1226 -object
->complex->y
, object
);
1228 o_list_mirror_world(0, 0, object
->complex->prim_objs
);
1230 switch(object
->complex->angle
) {
1232 object
->complex->angle
= 270;
1236 object
->complex->angle
= 90;
1241 object
->complex->mirror
= !object
->complex->mirror
;
1243 o_complex_translate_world(x
, y
, object
);
1246 /*! \brief search the pin with a given pin number
1247 * \par Function Description
1248 * This function searches a pin object inside the complex \a object.
1249 * The pin name is a character string \a pin.
1251 * \param object The complex object
1252 * \param pin The pin number (string) to find
1253 * \return a pin object if found, NULL otherwise
1255 OBJECT
*o_complex_return_pin_object(OBJECT
*object
, char *pin
)
1257 OBJECT
*o_current
=NULL
;
1260 g_return_val_if_fail(object
!= NULL
, NULL
);
1261 g_return_val_if_fail(((object
->type
== OBJ_COMPLEX
) ||
1262 (object
->type
== OBJ_PLACEHOLDER
)) , NULL
);
1263 g_return_val_if_fail(object
->complex != NULL
, NULL
);
1266 /* go inside complex objects */
1267 o_current
= object
->complex->prim_objs
;
1269 while ( o_current
!= NULL
) {
1270 switch(o_current
->type
) {
1272 /* Search for the pin making sure that */
1273 /* any found attribute starts with "pinnumber" */
1274 found
= o_attrib_search_attrib_value(o_current
->attribs
, pin
,
1278 printf("%s: %s\n", found
->name
, o_text_get_string(found
));
1284 o_current
=o_current
->next
;
1289 /*! \brief check the symversion of a complex object
1290 * \par Function Description
1291 * This function compares the symversion of a symbol with it's
1292 * earlier saved symversion in a schematic.
1293 * Major symversion changes are added to the toplevel object
1294 * (toplevel->major_changed_refdes), minor changes are reported
1295 * to the messaging system.
1297 * \param toplevel The TOPLEVEL object
1298 * \param object The complex OBJECT
1301 o_complex_check_symversion(TOPLEVEL
*toplevel
, OBJECT
const *object
)
1303 char *inside
= NULL
;
1304 char *outside
= NULL
;
1305 char *refdes
= NULL
;
1306 double inside_value
= -1.0;
1307 double outside_value
= -1.0;
1308 char *err_check
= NULL
;
1309 int inside_present
= FALSE
;
1310 int outside_present
= FALSE
;
1311 double inside_major
, inside_minor
;
1312 double outside_major
, outside_minor
;
1314 g_return_if_fail (object
!= NULL
);
1315 g_return_if_fail ((object
->type
== OBJ_COMPLEX
||
1316 object
->type
== OBJ_PLACEHOLDER
));
1317 g_return_if_fail (object
->complex != NULL
);
1319 /* first look on the inside for the symversion= attribute */
1320 inside
= o_attrib_search_object(object
, "symversion", 0);
1322 /* now look for the symversion= attached to object */
1323 outside
= o_attrib_search_attrib_name(object
->attribs
, "symversion", 0);
1325 /* get the uref for future use */
1326 refdes
= o_attrib_search_attrib_name(object
->attribs
, "refdes", 0);
1329 refdes
= g_strdup ("unknown");
1334 inside_value
= strtod(inside
, &err_check
);
1335 if (inside_value
== 0 && inside
== err_check
)
1339 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1340 "\tCould not parse symbol file symversion=%s\n"),
1343 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1344 "\tCould not parse symbol file symversion=\n"),
1349 inside_present
= TRUE
;
1351 inside_present
= FALSE
; /* attribute not inside */
1356 outside_value
= strtod(outside
, &err_check
);
1357 if (outside_value
== 0 && outside
== err_check
)
1359 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1360 "\tCould not parse attached symversion=%s\n"),
1364 outside_present
= TRUE
;
1366 outside_present
= FALSE
; /* attribute not outside */
1370 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object
->name
,
1371 inside_value
, outside_value
);
1374 /* symversion= is not present anywhere */
1375 if (!inside_present
&& !outside_present
)
1377 /* symbol is legacy and versioned okay */
1381 /* No symversion inside, but a version is outside, this is a weird case */
1382 if (!inside_present
&& outside_present
)
1384 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1385 "\tsymversion=%s attached to instantiated symbol, "
1386 "but no symversion= inside symbol file\n"),
1391 /* inside & not outside is a valid case, means symbol in library is newer */
1392 /* also if inside_value is greater than outside_value, then symbol in */
1393 /* library is newer */
1394 if ((inside_present
&& !outside_present
) ||
1395 ((inside_present
&& outside_present
) && (inside_value
> outside_value
)))
1398 s_log_message(_("WARNING: Symbol version mismatch on refdes %s (%s):\n"
1399 "\tSymbol in library is newer than "
1400 "instantiated symbol\n"),
1401 refdes
, object
->complex_basename
);
1403 /* break up the version values into major.minor numbers */
1404 inside_major
= floor(inside_value
);
1405 inside_minor
= inside_value
- inside_major
;
1407 if (outside_present
)
1409 outside_major
= floor(outside_value
);
1410 outside_minor
= outside_value
- outside_major
;
1412 /* symversion was not attached to the symbol, set all to zero */
1413 outside_major
= 0.0;
1414 outside_minor
= 0.0;
1415 outside_value
= 0.0;
1419 printf("i: %f %f %f\n", inside_value
, inside_major
, inside_minor
);
1420 printf("o: %f %f %f\n", outside_value
, outside_major
, outside_minor
);
1423 if (inside_major
> outside_major
)
1426 s_log_message(_("\tMAJOR VERSION CHANGE (file %.3f, "
1427 "instantiated %.3f, %s)!\n"),
1428 inside_value
, outside_value
, refdes
);
1430 /* add the refdes to the major_changed_refdes GList */
1431 /* make sure refdes_copy is freed somewhere */
1432 refdes_copy
= g_strconcat (refdes
, " (",
1433 object
->complex_basename
,
1435 toplevel
->major_changed_refdes
=
1436 g_list_append(toplevel
->major_changed_refdes
, refdes_copy
);
1438 /* don't bother checking minor changes if there are major ones*/
1442 if (inside_minor
> outside_minor
)
1444 s_log_message(_("\tMinor version change (file %.3f, "
1445 "instantiated %.3f)\n"),
1446 inside_value
, outside_value
);
1452 /* outside value is greater than inside value, this is weird case */
1453 if ((inside_present
&& outside_present
) && (outside_value
> inside_value
))
1455 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1456 "\tInstantiated symbol is newer than "
1457 "symbol in library\n"),
1462 /* if inside_value and outside_value match, then symbol versions are okay */
1470 /*! \brief Calculates the distance between the given point and the closest
1471 * point on an object within the complex object.
1473 * \param [in] complex The complex of the OBJECT
1474 * \param [in] x The x coordinate of the given point.
1475 * \param [in] y The y coordinate of the given point.
1476 * \return The shortest distance from the object to the point. If the
1477 * distance cannot be calculated, this function returns a really large
1478 * number (G_MAXDOUBLE). With an invalid parameter, this function returns
1481 gdouble
o_complex_shortest_distance(COMPLEX
const *complex, gint x
, gint y
)
1484 gdouble shortest_distance
= G_MAXDOUBLE
;
1487 if (complex == NULL
) {
1488 g_critical("o_complex_shortest_distance(): complex == NULL\n");
1492 temp
= complex->prim_objs
;
1498 while (temp
!= NULL
) {
1499 distance
= o_shortest_distance(temp
, x
, y
);
1501 shortest_distance
= min(shortest_distance
, distance
);
1506 return shortest_distance
;