1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
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.
38 #include "libgeda_priv.h"
40 #ifdef HAVE_LIBDMALLOC
44 /*! Default setting for complex draw function. */
45 void (*complex_draw_func
)() = NULL
;
48 /*! \brief Return the bounds of the given object.
49 * \par Given an object, calculate the bounds coordinates.
50 * \param [in] toplevel The toplevel structure.
51 * \param [in] o_current The object to look the bounds for.
52 * \param [out] rleft pointer to the left coordinate of the object.
53 * \param [out] rtop pointer to the top coordinate of the object.
54 * \param [out] rright pointer to the right coordinate of the object.
55 * \param [out] rbottom pointer to the bottom coordinate of the object.
56 * \return If any bounds were found for the object
57 * \retval 0 No bound was found
58 * \retval 1 Bound was found
60 int world_get_single_object_bounds(TOPLEVEL
*toplevel
, OBJECT
*o_current
,
61 int *rleft
, int *rtop
, int *rright
, int *rbottom
)
63 if (o_current
!= NULL
) {
64 switch(o_current
->type
) {
66 /* only do bounding boxes for visible or doing show_hidden_text*/
67 /* you might lose some attrs though */
68 if (! (o_current
->visibility
== VISIBLE
||
69 toplevel
->show_hidden_text
)) {
72 /* This case falls through intentionally */
83 case(OBJ_PLACEHOLDER
):
84 if (!o_current
->w_bounds_valid
) {
85 o_recalc_single_object (toplevel
, 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
;
104 /*! \brief Return the bounds of the given GList of objects.
105 * \par Given a list of objects, calcule the bounds coordinates.
106 * \param [in] toplevel The TOPLEVEL structure.
107 * \param [in] head The list of objects to look the bounds for.
108 * \param [out] left pointer to the left coordinate of the object.
109 * \param [out] top pointer to the top coordinate of the object.
110 * \param [out] right pointer to the right coordinate of the object.
111 * \param [out] bottom pointer to the bottom coordinate of the object.
112 * \return If any bounds were found for the list of objects
113 * \retval 0 No bounds were found
114 * \retval 1 Bound was found
116 int world_get_object_glist_bounds(TOPLEVEL
*toplevel
, const GList
*head
,
117 int *left
, int *top
, int *right
, int *bottom
)
119 const GList
*s_current
=NULL
;
120 OBJECT
*o_current
=NULL
;
121 int rleft
, rtop
, rright
, rbottom
;
126 /* Find the first object with bounds, and set the bounds variables, then expand as necessary */
127 while ( s_current
!= NULL
) {
128 o_current
= (OBJECT
*) s_current
->data
;
129 g_assert (o_current
!= NULL
);
130 if ( world_get_single_object_bounds( toplevel
, o_current
, &rleft
, &rtop
, &rright
, &rbottom
) ) {
132 *left
= min( *left
, rleft
);
133 *top
= min( *top
, rtop
);
134 *right
= max( *right
, rright
);
135 *bottom
= max( *bottom
, rbottom
);
144 s_current
= g_list_next (s_current
);
149 /*! \brief Queries the bounds of a complex object.
150 * \par Function Description
151 * This function returns the bounding box of the complex object
154 * \param [in] toplevel The toplevel environment.
155 * \param [in] complex The complex object.
156 * \param [out] left The leftmost edge of the bounding box (in
158 * \param [out] top The upper edge of the bounding box (in
160 * \param [out] right The rightmost edge of the bounding box (in
162 * \param [out] bottom The bottom edge of the bounding box (in
165 void world_get_complex_bounds(TOPLEVEL
*toplevel
, OBJECT
*complex,
166 int *left
, int *top
, int *right
, int *bottom
)
168 g_return_if_fail (complex != NULL
&&
169 (complex->type
== OBJ_COMPLEX
||
170 complex->type
== OBJ_PLACEHOLDER
) &&
171 complex->complex != NULL
);
173 world_get_object_glist_bounds (toplevel
, complex->complex->prim_objs
,
174 left
, top
, right
, bottom
);
177 /*! \brief get the position of the complex base point
178 * \par Function Description
179 * This function gets the position of the base point of a complex object.
181 * \param [in] toplevel The toplevel environment.
182 * \param [out] x pointer to the x-position
183 * \param [out] y pointer to the y-position
184 * \param [in] object The object to get the position.
185 * \return TRUE if successfully determined the position, FALSE otherwise
187 gboolean
o_complex_get_position (TOPLEVEL
*toplevel
, gint
*x
, gint
*y
,
190 *x
= object
->complex->x
;
191 *y
= object
->complex->y
;
195 /*! \brief check whether an object is a attributes
196 * \par Function Description
197 * This function checks if an object should be promoted.
198 * An attribute object is promotable if it's promoted by default, or the user
199 * has configered it to promote an attribute.
201 * \param [in] toplevel The TOPLEVEL object
202 * \param [in] object The attribute object to check
203 * \return TRUE if the object is a eligible attribute, FALSE otherwise
205 static int o_complex_is_eligible_attribute (TOPLEVEL
*toplevel
, OBJECT
*object
)
208 int promotableAttribute
= FALSE
;
210 /* always promote symversion= attribute, even if it is invisible */
211 if (strncmp(object
->text
->string
, "symversion=", 11) == 0)
214 /* check list against attributes which can be promoted */
215 if (toplevel
->always_promote_attributes
!= NULL
) {
216 if (o_attrib_get_name_value (object
, &name
, NULL
)) {
217 if (g_list_find_custom(toplevel
->always_promote_attributes
,
218 name
, (GCompareFunc
) strcmp
) != NULL
) {
219 /* Name of the attribute was in the always promote attributes list */
220 promotableAttribute
= TRUE
;
224 if (promotableAttribute
)
229 /* object is invisible and we do not want to promote invisible text */
230 if (object
->visibility
== INVISIBLE
&& toplevel
->promote_invisible
== FALSE
)
231 return FALSE
; /* attribute not eligible for promotion */
233 /* yup, attribute can be promoted */
237 /*! \brief get the embedded state of an complex object
238 * \par Function Description
239 * Checks and returns the status of the complex object.
241 * \param o_current The object to check
242 * \return 1 if embedded, 0 otherwise
244 int o_complex_is_embedded(OBJECT
*o_current
)
246 g_return_val_if_fail(o_current
!= NULL
, 0);
248 if(o_current
->complex == NULL
)
251 if (o_current
->complex_embedded
) {
259 /*! \brief Get attributes eligible for promotion from inside a complex
261 * \par Function Description
262 * Returns a GList of OBJECTs which are eligible for promotion from
263 * within the passed complex OBJECT.
265 * If detach is TRUE, the function removes these attribute objects from
266 * the prim_objs of the complex. It detached, the returned OBJECTs are
267 * isolated from each other, having their next and prev pointers set to NULL.
269 * If detach is FALSE, the OBJECTs are left in place. Their next and prev
270 * pointers form part of the complex's prim_objs linked list.
272 * \param [in] toplevel The toplevel environment.
273 * \param [in] object The complex object being modified.
274 * \param [in] detach Should the attributes be detached?
275 * \returns A linked list of OBJECTs to promote.
277 static GList
*o_complex_get_promotable (TOPLEVEL
*toplevel
, OBJECT
*object
, int detach
)
279 GList
*promoted
= NULL
;
284 if (!toplevel
->attribute_promotion
) /* controlled through rc file */
287 attribs
= o_attrib_find_floating_attribs (object
->complex->prim_objs
);
289 for (iter
= attribs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
292 /* Is it an attribute we want to promote? */
293 if (!o_complex_is_eligible_attribute(toplevel
, tmp
))
298 object
->complex->prim_objs
=
299 g_list_remove (object
->complex->prim_objs
, tmp
);
302 promoted
= g_list_prepend (promoted
, tmp
);
305 g_list_free (attribs
);
307 promoted
= g_list_reverse (promoted
);
312 /*! \brief Promote attributes from a complex OBJECT
313 * \par Function Description
314 * Selects promotable attributes from \a object, and returns a new
315 * #GList containing them (suitable for appending to a #PAGE).
317 * \param [in] toplevel The #TOPLEVEL environment.
318 * \param [in] object The complex #OBJECT to promote from.
319 * \return A #GList of promoted attributes.
321 GList
*o_complex_promote_attribs (TOPLEVEL
*toplevel
, OBJECT
*object
)
323 GList
*promoted
= NULL
;
324 GList
*promotable
= NULL
;
327 promotable
= o_complex_get_promotable (toplevel
, object
, FALSE
);
329 /* Run through the attributes deciding if we want to keep them (in
330 * which case we copy them and make them invisible) or if we want to
332 if (toplevel
->keep_invisible
) {
333 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
334 OBJECT
*o_kept
= (OBJECT
*) iter
->data
;
335 OBJECT
*o_copy
= o_object_copy (toplevel
, o_kept
);
336 o_set_visibility (toplevel
, o_kept
, INVISIBLE
);
337 o_copy
->parent
= NULL
;
338 promoted
= g_list_prepend (promoted
, o_copy
);
340 promoted
= g_list_reverse (promoted
);
342 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
343 OBJECT
*o_removed
= (OBJECT
*) iter
->data
;
344 o_removed
->parent
= NULL
;
345 object
->complex->prim_objs
=
346 g_list_remove (object
->complex->prim_objs
, o_removed
);
348 promoted
= promotable
;
351 /* Attach promoted attributes to the original complex object */
352 o_attrib_attach_list (toplevel
, promoted
, object
, TRUE
);
358 /*! \brief Delete or hide promotable from the passed OBJECT
360 * \par Function Description
361 * Deletes or hides promotable attributes from the passed OBJECT.
362 * This is used when loading symbols during the load of a schematic from
363 * disk. The schematic will already contain local copies of symbol's
364 * promotable objects, so we delete or hide the symbol's copies.
366 * Deletion / hiding is dependant on the setting of
367 * toplevel->keep_invisible. If true, attributes eligible for
368 * promotion are kept in memory but flagged as invisible.
370 * \param [in] toplevel The toplevel environment.
371 * \param [in] object The complex object being altered.
373 static void o_complex_remove_promotable_attribs (TOPLEVEL
*toplevel
, OBJECT
*object
)
375 GList
*promotable
, *iter
;
377 promotable
= o_complex_get_promotable (toplevel
, object
, FALSE
);
379 if (promotable
== NULL
)
382 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
383 OBJECT
*a_object
= iter
->data
;
384 if (toplevel
->keep_invisible
== TRUE
) { /* Hide promotable attributes */
385 o_set_visibility (toplevel
, a_object
, INVISIBLE
);
386 } else { /* Delete promotable attributes */
387 object
->complex->prim_objs
=
388 g_list_remove (object
->complex->prim_objs
, a_object
);
389 s_delete_object (toplevel
, a_object
);
393 g_list_free (promotable
);
399 * \par Function Description
402 OBJECT
*o_complex_new(TOPLEVEL
*toplevel
,
404 int color
, int x
, int y
, int angle
,
405 int mirror
, const CLibSymbol
*clib
,
406 const gchar
*basename
,
409 OBJECT
*new_node
=NULL
;
410 OBJECT
*new_prim_obj
;
413 int loaded_normally
= FALSE
;
415 gchar
*buffer
= NULL
;
417 new_node
= s_basic_new_object(type
, "complex");
420 new_node
->complex_basename
= g_strdup (s_clib_symbol_get_name (clib
));
422 new_node
->complex_basename
= g_strdup (basename
);
426 new_node
->complex_embedded
= FALSE
;
427 new_node
->color
= color
;
429 new_node
->complex = (COMPLEX
*) g_malloc(sizeof(COMPLEX
));
430 new_node
->complex->angle
= angle
;
431 new_node
->complex->mirror
= mirror
;
432 new_node
->complex->x
= x
;
433 new_node
->complex->y
= y
;
435 new_node
->draw_func
= complex_draw_func
;
438 new_node
->sel_func
= select_func
;
440 new_node
->sel_func
= NULL
;
445 /* get the symbol data */
447 buffer
= s_clib_symbol_get_data (clib
);
450 if (clib
== NULL
|| buffer
== NULL
) {
452 char *not_found_text
= NULL
;
453 int left
, right
, top
, bottom
;
454 int x_offset
, y_offset
;
456 /* filename was NOT found */
457 loaded_normally
= FALSE
;
459 /* Put placeholder into object list. Changed by SDB on
460 * 1.19.2005 to fix problem that symbols were silently
461 * deleted by gattrib when RC files were messed up. */
462 new_node
->type
= OBJ_PLACEHOLDER
;
464 /* Mark the origin of the missing component */
465 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
466 DETACHED_ATTRIBUTE_COLOR
,
467 x
- 50, y
, x
+ 50, y
);
468 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
469 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
470 DETACHED_ATTRIBUTE_COLOR
,
471 x
, y
+ 50, x
, y
- 50);
472 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
474 /* Add some useful text */
476 g_strdup_printf (_("Component not found:\n %s"),
477 new_node
->complex_basename
);
478 new_prim_obj
= o_text_new(toplevel
,
479 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
480 x
+ NOT_FOUND_TEXT_X
,
481 y
+ NOT_FOUND_TEXT_Y
, LOWER_LEFT
, 0,
483 VISIBLE
, SHOW_NAME_VALUE
);
484 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
485 g_free(not_found_text
);
487 /* figure out where to put the hazard triangle */
488 world_get_text_bounds (toplevel
, new_prim_obj
, &left
, &top
, &right
, &bottom
);
489 x_offset
= (right
- left
) / 4;
490 y_offset
= bottom
- top
+ 100; /* 100 is just an additional offset */
492 /* add hazard triangle */
493 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
494 DETACHED_ATTRIBUTE_COLOR
,
495 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
496 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
497 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
498 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
499 o_set_line_options(toplevel
, new_prim_obj
, END_ROUND
, TYPE_SOLID
,
501 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
502 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
503 DETACHED_ATTRIBUTE_COLOR
,
504 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
505 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
506 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
507 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500);
508 o_set_line_options(toplevel
, new_prim_obj
, END_ROUND
, TYPE_SOLID
,
510 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
511 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
512 DETACHED_ATTRIBUTE_COLOR
,
513 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
514 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500,
515 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
516 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
517 o_set_line_options(toplevel
, new_prim_obj
, END_ROUND
, TYPE_SOLID
,
519 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
520 new_prim_obj
= o_text_new(toplevel
,
521 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
522 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 270,
523 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 90,
524 LOWER_LEFT
, 0, "!", 18,
525 VISIBLE
, SHOW_NAME_VALUE
);
526 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
530 /* filename was found */
531 loaded_normally
= TRUE
;
533 /* add connections till translated */
534 prim_objs
= o_read_buffer (toplevel
, prim_objs
, buffer
, -1, new_node
->complex_basename
);
540 /* do not mirror/rotate/translate/connect the primitive objects if the
541 * component was not loaded via o_read
543 if (loaded_normally
== TRUE
) {
545 o_glist_mirror_world (toplevel
, 0, 0, prim_objs
);
548 o_glist_rotate_world (toplevel
, 0, 0, angle
, prim_objs
);
549 o_glist_translate_world (toplevel
, x
, y
, prim_objs
);
552 new_node
->complex->prim_objs
= prim_objs
;
554 /* set the parent field now */
555 for (iter
= prim_objs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
556 OBJECT
*tmp
= iter
->data
;
557 tmp
->parent
= new_node
;
560 o_complex_recalc(toplevel
, new_node
);
565 /*! \brief create a new embedded object
566 * \par Function Description
567 * This function creates a new embedded object.
569 * \param [in] toplevel The TOPLEVEL object
570 * \param [in] type The type of the object (usually OBJ_COMLEX)
571 * \param [in] color The color of the object
572 * \param [in] x The x location of the complex object
573 * \param [in] y The y location of the complex object
574 * \param [in] angle The rotation angle
575 * \param [in] mirror The mirror status
576 * \param [in] basename The basic name the embedded was created of
577 * \param [in] selectable whether the object can be selected with the mouse
578 * \return a new complex object
580 OBJECT
*o_complex_new_embedded(TOPLEVEL
*toplevel
,
581 char type
, int color
, int x
, int y
, int angle
, int mirror
,
582 const gchar
*basename
, int selectable
)
584 OBJECT
*new_node
=NULL
;
586 new_node
= s_basic_new_object(type
, "complex");
588 new_node
->complex = (COMPLEX
*) g_malloc(sizeof(COMPLEX
));
589 new_node
->complex->x
= x
;
590 new_node
->complex->y
= y
;
592 new_node
->complex->angle
= angle
;
593 new_node
->complex->mirror
= mirror
;
595 new_node
->complex_basename
= g_strdup(basename
);
597 new_node
->complex_embedded
= TRUE
;
599 new_node
->color
= color
;
601 new_node
->draw_func
= complex_draw_func
;
603 /* (for a title block) an object that isn't selectable */
605 new_node
->sel_func
= select_func
;
607 new_node
->sel_func
= NULL
;
610 new_node
->complex->prim_objs
= NULL
;
612 /* don't have to translate/rotate/mirror here at all since the */
613 /* object is in place */
617 /*! \brief update the visual boundaries of the complex object
618 * \par Function Description
619 * This function updates the boundaries of the object \a o_current.
621 * \param [in] toplevel The TOPLEVEL object
622 * \param [in] o_current The OBJECT to update
624 void o_complex_recalc(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
626 int left
, right
, top
, bottom
;
628 /* realc routine Add this somewhere */
630 /* o_recalc(toplevel, o_current->complex);*/
632 if ((!o_current
) || (o_current
->type
!= OBJ_COMPLEX
&& o_current
->type
!= OBJ_PLACEHOLDER
))
635 world_get_complex_bounds(toplevel
, o_current
, &left
, &top
, &right
, &bottom
);
636 o_current
->w_left
= left
;
637 o_current
->w_top
= top
;
638 o_current
->w_right
= right
;
639 o_current
->w_bottom
= bottom
;
640 o_current
->w_bounds_valid
= TRUE
;
643 /*! \brief read a complex object from a char buffer
644 * \par Function Description
645 * This function reads a complex object from the buffer \a buf.
646 * If the complex object was read successfully, a new object is
647 * allocated and appended to the \a object_list.
649 * \param [in] toplevel The TOPLEVEL object
650 * \param [in] buf a text buffer (usually a line of a schematic file)
651 * \param [in] release_ver The release number gEDA
652 * \param [in] fileformat_ver a integer value of the file format
653 * \return The object list
655 * \todo Don't use fixed-length string for symbol basename
657 OBJECT
*o_complex_read (TOPLEVEL
*toplevel
,
658 char buf
[], unsigned int release_ver
,
659 unsigned int fileformat_ver
)
666 char basename
[256]; /* FIXME This is a hack */
671 sscanf(buf
, "%c %d %d %d %d %d %s\n",
672 &type
, &x1
, &y1
, &selectable
, &angle
, &mirror
, basename
);
683 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
);
695 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
);
698 if (strncmp(basename
, "EMBEDDED", 8) == 0) {
700 new_obj
= o_complex_new_embedded(toplevel
, type
,
701 DEFAULT_COLOR
, x1
, y1
, angle
, mirror
,
706 const CLibSymbol
*clib
= s_clib_get_symbol_by_name (basename
);
708 new_obj
= o_complex_new(toplevel
, type
,
712 basename
, selectable
);
713 /* Delete or hide attributes eligible for promotion inside the complex */
714 o_complex_remove_promotable_attribs (toplevel
, new_obj
);
720 /*! \brief Create a string representation of the complex object
721 * \par Function Description
722 * This function takes a complex \a object and return a string
723 * according to the file format definition.
725 * \param [in] object a complex OBJECT
726 * \return the string representation of the complex OBJECT
728 char *o_complex_save(OBJECT
*object
)
734 g_return_val_if_fail (object
!= NULL
, NULL
);
736 if (object
->sel_func
!= NULL
)
741 if ((object
->type
== OBJ_COMPLEX
) || (object
->type
== OBJ_PLACEHOLDER
)) {
742 basename
= g_strdup_printf ("%s%s",
743 object
->complex_embedded
? "EMBEDDED" : "",
744 object
->complex_basename
);
745 /* We force the object type to be output as OBJ_COMPLEX for both
746 * these object types. */
747 buf
= g_strdup_printf("%c %d %d %d %d %d %s", OBJ_COMPLEX
,
748 object
->complex->x
, object
->complex->y
,
749 selectable
, object
->complex->angle
,
750 object
->complex->mirror
, basename
);
757 /*! \brief move a complex object
758 * \par Function Description
759 * This function changes the position of a complex \a object.
761 * \param [in] toplevel The TOPLEVEL object
762 * \param [in] dx The x-distance to move the object
763 * \param [in] dy The y-distance to move the object
764 * \param [in] object The complex OBJECT to be moved
766 void o_complex_translate_world(TOPLEVEL
*toplevel
, int dx
, int dy
,
769 g_return_if_fail (object
!= NULL
&&
770 (object
->type
== OBJ_COMPLEX
||
771 object
->type
== OBJ_PLACEHOLDER
));
773 object
->complex->x
= object
->complex->x
+ dx
;
774 object
->complex->y
= object
->complex->y
+ dy
;
776 o_glist_translate_world (toplevel
, dx
, dy
, object
->complex->prim_objs
);
778 o_complex_recalc (toplevel
, object
);
781 /*! \brief Create a copy of a COMPLEX object
782 * \par Function Description
783 * This function creates a copy of the complex object \a o_current.
785 * \param [in] toplevel The TOPLEVEL object
786 * \param [in] o_current The object that is copied
787 * \return a new COMPLEX object
789 OBJECT
*o_complex_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
794 g_return_val_if_fail(o_current
!= NULL
, NULL
);
796 o_new
= s_basic_new_object(o_current
->type
, "complex");
797 o_new
->color
= o_current
->color
;
798 o_new
->complex_basename
= g_strdup(o_current
->complex_basename
);
799 o_new
->complex_embedded
= o_current
->complex_embedded
;
800 o_new
->sel_func
= o_current
->sel_func
;
801 o_new
->draw_func
= o_current
->draw_func
;
803 o_new
->complex = g_malloc0(sizeof(COMPLEX
));
804 o_new
->complex->x
= o_current
->complex->x
;
805 o_new
->complex->y
= o_current
->complex->y
;
806 o_new
->complex->angle
= o_current
->complex->angle
;
807 o_new
->complex->mirror
= o_current
->complex->mirror
;
809 /* Copy contents and set the parent pointers on the copied objects. */
810 o_new
->complex->prim_objs
=
811 o_glist_copy_all (toplevel
, o_current
->complex->prim_objs
,
814 for (iter
= o_new
->complex->prim_objs
;
816 iter
= g_list_next (iter
)) {
817 ((OBJECT
*) iter
->data
)->parent
= o_new
;
820 /* Recalculate bounds */
821 o_complex_recalc(toplevel
, o_new
);
823 /* Delete or hide attributes eligible for promotion inside the complex */
824 o_complex_remove_promotable_attribs (toplevel
, o_new
);
826 s_slot_update_object (toplevel
, o_new
);
828 /* deal with stuff that has changed */
830 /* here you need to create a list of attributes which need to be
831 * connected to the new list, probably make an attribute list and
832 * fill it with sid's of the attributes */
838 /*! \todo Finish function documentation!!!
840 * \par Function Description
843 void o_complex_rotate_world(TOPLEVEL
*toplevel
,
844 int centerx
, int centery
,
845 int angle
, OBJECT
*object
)
850 g_return_if_fail (object
!=NULL
);
851 g_return_if_fail ((object
->type
== OBJ_COMPLEX
) ||
852 (object
->type
== OBJ_PLACEHOLDER
));
854 x
= object
->complex->x
+ (-centerx
);
855 y
= object
->complex->y
+ (-centery
);
857 rotate_point_90(x
, y
, angle
, &newx
, &newy
);
859 x
= newx
+ (centerx
);
860 y
= newy
+ (centery
);
862 o_complex_translate_world(toplevel
,
864 -object
->complex->y
, object
);
865 o_glist_rotate_world (toplevel
, 0, 0, angle
, object
->complex->prim_objs
);
867 object
->complex->x
= 0;
868 object
->complex->y
= 0;
870 o_complex_translate_world(toplevel
, x
, y
, object
);
872 object
->complex->angle
= ( object
->complex->angle
+ angle
) % 360;
876 /*! \todo Finish function documentation!!!
878 * \par Function Description
881 void o_complex_mirror_world(TOPLEVEL
*toplevel
,
882 int world_centerx
, int world_centery
,
887 g_return_if_fail( object
!= NULL
);
888 g_return_if_fail( (object
->type
== OBJ_COMPLEX
||
889 object
->type
== OBJ_PLACEHOLDER
) );
890 g_return_if_fail( object
->complex != NULL
);
892 x
= 2 * world_centerx
- object
->complex->x
;
893 y
= object
->complex->y
;
895 o_complex_translate_world(toplevel
,
897 -object
->complex->y
, object
);
899 o_glist_mirror_world (toplevel
, 0, 0, object
->complex->prim_objs
);
901 switch(object
->complex->angle
) {
903 object
->complex->angle
= 270;
907 object
->complex->angle
= 90;
912 object
->complex->mirror
= !object
->complex->mirror
;
914 o_complex_translate_world(toplevel
, x
, y
, object
);
918 /*! \brief Find a pin with a particular attribute.
919 * \par Function Description
920 * Search for a pin inside the given complex which has an attribute
921 * matching those passed.
923 * \param [in] object complex OBJECT whos pins to search.
924 * \param [in] name the attribute name to search for.
925 * \param [in] wanted_value the attribute value to search for.
926 * \return The pin OBJECT with the given attribute, NULL otherwise.
928 OBJECT
*o_complex_find_pin_by_attribute (OBJECT
*object
, char *name
, char *wanted_value
)
935 g_return_val_if_fail (object
!= NULL
, NULL
);
936 g_return_val_if_fail (object
->type
== OBJ_COMPLEX
||
937 object
->type
== OBJ_PLACEHOLDER
, NULL
);
939 for (iter
= object
->complex->prim_objs
; iter
!= NULL
;
940 iter
= g_list_next (iter
)) {
941 o_current
= iter
->data
;
943 if (o_current
->type
!= OBJ_PIN
)
946 value
= o_attrib_search_object_attribs_by_name (o_current
, name
, 0);
947 found
= (value
!= NULL
&& strcmp (value
, wanted_value
) == 0);
958 /*! \brief check the symversion of a complex object
959 * \par Function Description
960 * This function compares the symversion of a symbol with it's
961 * earlier saved symversion in a schematic.
962 * Major symversion changes are added to the toplevel object
963 * (toplevel->major_changed_refdes), minor changes are reported
964 * to the messaging system.
966 * \param toplevel The TOPLEVEL object
967 * \param object The complex OBJECT
970 o_complex_check_symversion(TOPLEVEL
* toplevel
, OBJECT
* object
)
973 char *outside
= NULL
;
975 double inside_value
= -1.0;
976 double outside_value
= -1.0;
977 char *err_check
= NULL
;
978 int inside_present
= FALSE
;
979 int outside_present
= FALSE
;
980 double inside_major
, inside_minor
;
981 double outside_major
, outside_minor
;
983 g_return_if_fail (object
!= NULL
);
984 g_return_if_fail ((object
->type
== OBJ_COMPLEX
||
985 object
->type
== OBJ_PLACEHOLDER
));
986 g_return_if_fail (object
->complex != NULL
);
988 /* first look on the inside for the symversion= attribute */
989 inside
= o_attrib_search_inherited_attribs_by_name (object
, "symversion", 0);
991 /* now look for the symversion= attached to object */
992 outside
= o_attrib_search_attached_attribs_by_name (object
, "symversion", 0);
994 /* get the uref for future use */
995 refdes
= o_attrib_search_object_attribs_by_name(object
, "refdes", 0);
998 refdes
= g_strdup ("unknown");
1003 inside_value
= strtod(inside
, &err_check
);
1004 if (inside_value
== 0 && inside
== err_check
)
1008 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1009 "\tCould not parse symbol file symversion=%s\n"),
1012 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1013 "\tCould not parse symbol file symversion=\n"),
1018 inside_present
= TRUE
;
1020 inside_present
= FALSE
; /* attribute not inside */
1025 outside_value
= strtod(outside
, &err_check
);
1026 if (outside_value
== 0 && outside
== err_check
)
1028 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1029 "\tCould not parse attached symversion=%s\n"),
1033 outside_present
= TRUE
;
1035 outside_present
= FALSE
; /* attribute not outside */
1039 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object
->name
,
1040 inside_value
, outside_value
);
1043 /* symversion= is not present anywhere */
1044 if (!inside_present
&& !outside_present
)
1046 /* symbol is legacy and versioned okay */
1050 /* No symversion inside, but a version is outside, this is a weird case */
1051 if (!inside_present
&& outside_present
)
1053 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1054 "\tsymversion=%s attached to instantiated symbol, "
1055 "but no symversion= inside symbol file\n"),
1060 /* inside & not outside is a valid case, means symbol in library is newer */
1061 /* also if inside_value is greater than outside_value, then symbol in */
1062 /* library is newer */
1063 if ((inside_present
&& !outside_present
) ||
1064 ((inside_present
&& outside_present
) && (inside_value
> outside_value
)))
1067 s_log_message(_("WARNING: Symbol version mismatch on refdes %s (%s):\n"
1068 "\tSymbol in library is newer than "
1069 "instantiated symbol\n"),
1070 refdes
, object
->complex_basename
);
1072 /* break up the version values into major.minor numbers */
1073 inside_major
= floor(inside_value
);
1074 inside_minor
= inside_value
- inside_major
;
1076 if (outside_present
)
1078 outside_major
= floor(outside_value
);
1079 outside_minor
= outside_value
- outside_major
;
1081 /* symversion was not attached to the symbol, set all to zero */
1082 outside_major
= 0.0;
1083 outside_minor
= 0.0;
1084 outside_value
= 0.0;
1088 printf("i: %f %f %f\n", inside_value
, inside_major
, inside_minor
);
1089 printf("o: %f %f %f\n", outside_value
, outside_major
, outside_minor
);
1092 if (inside_major
> outside_major
)
1095 s_log_message(_("\tMAJOR VERSION CHANGE (file %.3f, "
1096 "instantiated %.3f, %s)!\n"),
1097 inside_value
, outside_value
, refdes
);
1099 /* add the refdes to the major_changed_refdes GList */
1100 /* make sure refdes_copy is freed somewhere */
1101 refdes_copy
= g_strconcat (refdes
, " (",
1102 object
->complex_basename
,
1104 toplevel
->major_changed_refdes
=
1105 g_list_append(toplevel
->major_changed_refdes
, refdes_copy
);
1107 /* don't bother checking minor changes if there are major ones*/
1111 if (inside_minor
> outside_minor
)
1113 s_log_message(_("\tMinor version change (file %.3f, "
1114 "instantiated %.3f)\n"),
1115 inside_value
, outside_value
);
1121 /* outside value is greater than inside value, this is weird case */
1122 if ((inside_present
&& outside_present
) && (outside_value
> inside_value
))
1124 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1125 "\tInstantiated symbol is newer than "
1126 "symbol in library\n"),
1131 /* if inside_value and outside_value match, then symbol versions are okay */
1139 /*! \brief Calculates the distance between the given point and the closest
1140 * point on an object within the complex object.
1142 * \note When querying the distance to our child objects, we always
1143 * force treating them as solid filled.
1144 * We ignore the force_solid argument to this function.
1146 * \param [in] object The complex OBJECT.
1147 * \param [in] x The x coordinate of the given point.
1148 * \param [in] y The y coordinate of the given point.
1149 * \param [in] force_solid If true, force treating the object as solid.
1150 * \return The shortest distance from the object to the point. If the
1151 * distance cannot be calculated, this function returns a really large
1152 * number (G_MAXDOUBLE). With an invalid parameter, this function returns
1155 double o_complex_shortest_distance (OBJECT
*object
, int x
, int y
,
1158 double shortest_distance
= G_MAXDOUBLE
;
1160 int found_line_bounds
= 0;
1164 g_return_val_if_fail (object
->complex != NULL
, G_MAXDOUBLE
);
1166 for (iter
= object
->complex->prim_objs
;
1167 iter
!= NULL
; iter
= g_list_next (iter
)) {
1168 OBJECT
*obj
= iter
->data
;
1170 /* Collect the bounds of any lines and arcs in the symbol */
1171 if ((obj
->type
== OBJ_LINE
|| obj
->type
== OBJ_ARC
) &&
1172 obj
->w_bounds_valid
) {
1174 if (found_line_bounds
) {
1175 line_bounds
.lower_x
= min (line_bounds
.lower_x
, obj
->w_left
);
1176 line_bounds
.lower_y
= min (line_bounds
.lower_y
, obj
->w_top
);
1177 line_bounds
.upper_x
= max (line_bounds
.upper_x
, obj
->w_right
);
1178 line_bounds
.upper_y
= max (line_bounds
.upper_y
, obj
->w_bottom
);
1180 line_bounds
.lower_x
= obj
->w_left
;
1181 line_bounds
.lower_y
= obj
->w_top
;
1182 line_bounds
.upper_x
= obj
->w_right
;
1183 line_bounds
.upper_y
= obj
->w_bottom
;
1184 found_line_bounds
= 1;
1187 distance
= o_shortest_distance_full (obj
, x
, y
, TRUE
);
1188 shortest_distance
= min (shortest_distance
, distance
);
1191 if (shortest_distance
== 0.0)
1192 return shortest_distance
;
1195 if (found_line_bounds
) {
1196 distance
= m_box_shortest_distance (&line_bounds
, x
, y
, TRUE
);
1197 shortest_distance
= min (shortest_distance
, distance
);
1200 return shortest_distance
;