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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
45 /*! \brief Return the bounds of the given object.
46 * \par Given an object, calculate the bounds coordinates.
47 * \param [in] toplevel The toplevel structure.
48 * \param [in] o_current The object to look the bounds for.
49 * \param [out] rleft pointer to the left coordinate of the object.
50 * \param [out] rtop pointer to the top coordinate of the object.
51 * \param [out] rright pointer to the right coordinate of the object.
52 * \param [out] rbottom pointer to the bottom coordinate of the object.
53 * \return If any bounds were found for the object
54 * \retval 0 No bound was found
55 * \retval 1 Bound was found
57 int world_get_single_object_bounds(TOPLEVEL
*toplevel
, OBJECT
*o_current
,
58 int *rleft
, int *rtop
, int *rright
, int *rbottom
)
60 if (o_current
!= NULL
) {
61 switch(o_current
->type
) {
63 /* only do bounding boxes for visible or doing show_hidden_text*/
64 /* you might lose some attrs though */
65 if (! (o_is_visible (toplevel
, o_current
) ||
66 toplevel
->show_hidden_text
)) {
69 /* This case falls through intentionally */
80 case(OBJ_PLACEHOLDER
):
81 if (!o_current
->w_bounds_valid
) {
82 o_recalc_single_object (toplevel
, o_current
);
83 if (!o_current
->w_bounds_valid
) {
87 *rleft
= o_current
->w_left
;
88 *rtop
= o_current
->w_top
;
89 *rright
= o_current
->w_right
;
90 *rbottom
= o_current
->w_bottom
;
101 /*! \brief Return the bounds of the given GList of objects.
102 * \par Given a list of objects, calcule the bounds coordinates.
103 * \param [in] toplevel The TOPLEVEL structure.
104 * \param [in] head The list of objects to look the bounds for.
105 * \param [out] left pointer to the left coordinate of the object.
106 * \param [out] top pointer to the top coordinate of the object.
107 * \param [out] right pointer to the right coordinate of the object.
108 * \param [out] bottom pointer to the bottom coordinate of the object.
109 * \return If any bounds were found for the list of objects
110 * \retval 0 No bounds were found
111 * \retval 1 Bound was found
113 int world_get_object_glist_bounds(TOPLEVEL
*toplevel
, const GList
*head
,
114 int *left
, int *top
, int *right
, int *bottom
)
116 const GList
*s_current
=NULL
;
117 OBJECT
*o_current
=NULL
;
118 int rleft
, rtop
, rright
, rbottom
;
123 /* Find the first object with bounds, and set the bounds variables, then expand as necessary */
124 while ( s_current
!= NULL
) {
125 o_current
= (OBJECT
*) s_current
->data
;
128 g_return_val_if_fail ((o_current
!= NULL
), found
);
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 ((!o_is_visible (toplevel
, object
)) &&
231 (toplevel
->promote_invisible
== FALSE
))
232 return FALSE
; /* attribute not eligible for promotion */
234 /* yup, attribute can be promoted */
238 /*! \brief get the embedded state of an complex object
239 * \par Function Description
240 * Checks and returns the status of the complex object.
242 * \param o_current The object to check
243 * \return 1 if embedded, 0 otherwise
245 int o_complex_is_embedded(OBJECT
*o_current
)
247 g_return_val_if_fail(o_current
!= NULL
, 0);
249 if(o_current
->complex == NULL
)
252 if (o_current
->complex_embedded
) {
260 /*! \brief Get attributes eligible for promotion from inside a complex
262 * \par Function Description
263 * Returns a GList of OBJECTs which are eligible for promotion from
264 * within the passed complex OBJECT.
266 * If detach is TRUE, the function removes these attribute objects
267 * from the prim_objs of the complex. If detach is FALSE, the
268 * OBJECTs are left in place.
270 * \param [in] toplevel The toplevel environment.
271 * \param [in] object The complex object being modified.
272 * \param [in] detach Should the attributes be detached?
273 * \returns A linked list of OBJECTs to promote.
275 GList
*o_complex_get_promotable (TOPLEVEL
*toplevel
, OBJECT
*object
, int detach
)
277 GList
*promoted
= NULL
;
282 if (!toplevel
->attribute_promotion
) /* controlled through rc file */
285 attribs
= o_attrib_find_floating_attribs (object
->complex->prim_objs
);
287 for (iter
= attribs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
290 /* Is it an attribute we want to promote? */
291 if (!o_complex_is_eligible_attribute(toplevel
, tmp
))
296 object
->complex->prim_objs
=
297 g_list_remove (object
->complex->prim_objs
, tmp
);
300 promoted
= g_list_prepend (promoted
, tmp
);
303 g_list_free (attribs
);
305 promoted
= g_list_reverse (promoted
);
310 /*! \brief Promote attributes from a complex OBJECT
311 * \par Function Description
312 * Selects promotable attributes from \a object, and returns a new
313 * #GList containing them (suitable for appending to a #PAGE).
315 * \param [in] toplevel The #TOPLEVEL environment.
316 * \param [in] object The complex #OBJECT to promote from.
317 * \return A #GList of promoted attributes.
319 GList
*o_complex_promote_attribs (TOPLEVEL
*toplevel
, OBJECT
*object
)
321 GList
*promoted
= NULL
;
322 GList
*promotable
= NULL
;
325 promotable
= o_complex_get_promotable (toplevel
, object
, FALSE
);
327 /* Run through the attributes deciding if we want to keep them (in
328 * which case we copy them and make them invisible) or if we want to
330 if (toplevel
->keep_invisible
) {
331 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
332 OBJECT
*o_kept
= (OBJECT
*) iter
->data
;
333 OBJECT
*o_copy
= o_object_copy (toplevel
, o_kept
);
334 o_set_visibility (toplevel
, o_kept
, INVISIBLE
);
335 o_copy
->parent
= NULL
;
336 promoted
= g_list_prepend (promoted
, o_copy
);
338 promoted
= g_list_reverse (promoted
);
340 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
341 OBJECT
*o_removed
= (OBJECT
*) iter
->data
;
342 o_removed
->parent
= NULL
;
343 object
->complex->prim_objs
=
344 g_list_remove (object
->complex->prim_objs
, o_removed
);
346 promoted
= promotable
;
347 /* Invalidate the object's bounds since we may have
348 * stolen objects from inside it. */
349 o_bounds_invalidate (toplevel
, object
);
352 /* Attach promoted attributes to the original complex object */
353 o_attrib_attach_list (toplevel
, promoted
, object
, TRUE
);
359 /*! \brief Delete or hide promotable from the passed OBJECT
361 * \par Function Description
362 * Deletes or hides promotable attributes from the passed OBJECT.
363 * This is used when loading symbols during the load of a schematic from
364 * disk. The schematic will already contain local copies of symbol's
365 * promotable objects, so we delete or hide the symbol's copies.
367 * Deletion / hiding is dependant on the setting of
368 * toplevel->keep_invisible. If true, attributes eligible for
369 * promotion are kept in memory but flagged as invisible.
371 * \param [in] toplevel The toplevel environment.
372 * \param [in] object The complex object being altered.
374 static void o_complex_remove_promotable_attribs (TOPLEVEL
*toplevel
, OBJECT
*object
)
376 GList
*promotable
, *iter
;
378 promotable
= o_complex_get_promotable (toplevel
, object
, FALSE
);
380 if (promotable
== NULL
)
383 for (iter
= promotable
; iter
!= NULL
; iter
= g_list_next (iter
)) {
384 OBJECT
*a_object
= iter
->data
;
385 if (toplevel
->keep_invisible
== TRUE
) { /* Hide promotable attributes */
386 o_set_visibility (toplevel
, a_object
, INVISIBLE
);
387 } else { /* Delete promotable attributes */
388 object
->complex->prim_objs
=
389 g_list_remove (object
->complex->prim_objs
, a_object
);
390 s_delete_object (toplevel
, a_object
);
394 o_bounds_invalidate (toplevel
, object
);
395 g_list_free (promotable
);
401 * \par Function Description
404 OBJECT
*o_complex_new(TOPLEVEL
*toplevel
,
406 int color
, int x
, int y
, int angle
,
407 int mirror
, const CLibSymbol
*clib
,
408 const gchar
*basename
,
411 OBJECT
*new_node
=NULL
;
412 OBJECT
*new_prim_obj
;
415 int loaded_normally
= FALSE
;
417 gchar
*buffer
= NULL
;
419 new_node
= s_basic_new_object(type
, "complex");
422 new_node
->complex_basename
= g_strdup (s_clib_symbol_get_name (clib
));
424 new_node
->complex_basename
= g_strdup (basename
);
428 new_node
->complex_embedded
= FALSE
;
429 new_node
->color
= color
;
430 new_node
->selectable
= selectable
;
432 new_node
->complex = (COMPLEX
*) g_malloc(sizeof(COMPLEX
));
433 new_node
->complex->angle
= angle
;
434 new_node
->complex->mirror
= mirror
;
435 new_node
->complex->x
= x
;
436 new_node
->complex->y
= y
;
440 /* get the symbol data */
442 buffer
= s_clib_symbol_get_data (clib
);
445 if (clib
== NULL
|| buffer
== NULL
) {
447 char *not_found_text
= NULL
;
448 int left
, right
, top
, bottom
;
449 int x_offset
, y_offset
;
451 /* filename was NOT found */
452 loaded_normally
= FALSE
;
454 /* Put placeholder into object list. Changed by SDB on
455 * 1.19.2005 to fix problem that symbols were silently
456 * deleted by gattrib when RC files were messed up. */
457 new_node
->type
= OBJ_PLACEHOLDER
;
459 /* Mark the origin of the missing component */
460 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
461 DETACHED_ATTRIBUTE_COLOR
,
462 x
- 50, y
, x
+ 50, y
);
463 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
464 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
465 DETACHED_ATTRIBUTE_COLOR
,
466 x
, y
+ 50, x
, y
- 50);
467 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
469 /* Add some useful text */
471 g_strdup_printf (_("Component not found:\n %s"),
472 new_node
->complex_basename
);
473 new_prim_obj
= o_text_new(toplevel
,
474 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
475 x
+ NOT_FOUND_TEXT_X
,
476 y
+ NOT_FOUND_TEXT_Y
, LOWER_LEFT
, 0,
478 VISIBLE
, SHOW_NAME_VALUE
);
479 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
480 g_free(not_found_text
);
482 /* figure out where to put the hazard triangle */
483 world_get_text_bounds (toplevel
, new_prim_obj
, &left
, &top
, &right
, &bottom
);
484 x_offset
= (right
- left
) / 4;
485 y_offset
= bottom
- top
+ 100; /* 100 is just an additional offset */
487 /* add hazard triangle */
488 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
489 DETACHED_ATTRIBUTE_COLOR
,
490 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
491 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
492 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
493 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
494 o_set_line_options(toplevel
, new_prim_obj
, END_ROUND
, TYPE_SOLID
,
496 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
497 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
498 DETACHED_ATTRIBUTE_COLOR
,
499 x
+ NOT_FOUND_TEXT_X
+ x_offset
,
500 y
+ NOT_FOUND_TEXT_Y
+ y_offset
,
501 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
502 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500);
503 o_set_line_options(toplevel
, new_prim_obj
, END_ROUND
, TYPE_SOLID
,
505 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
506 new_prim_obj
= o_line_new(toplevel
, OBJ_LINE
,
507 DETACHED_ATTRIBUTE_COLOR
,
508 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 300,
509 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 500,
510 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 600,
511 y
+ NOT_FOUND_TEXT_Y
+ y_offset
);
512 o_set_line_options(toplevel
, new_prim_obj
, END_ROUND
, TYPE_SOLID
,
514 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
515 new_prim_obj
= o_text_new(toplevel
,
516 OBJ_TEXT
, DETACHED_ATTRIBUTE_COLOR
,
517 x
+ NOT_FOUND_TEXT_X
+ x_offset
+ 270,
518 y
+ NOT_FOUND_TEXT_Y
+ y_offset
+ 90,
519 LOWER_LEFT
, 0, "!", 18,
520 VISIBLE
, SHOW_NAME_VALUE
);
521 prim_objs
= g_list_append (prim_objs
, new_prim_obj
);
525 /* filename was found */
526 loaded_normally
= TRUE
;
528 /* add connections till translated */
529 prim_objs
= o_read_buffer (toplevel
, prim_objs
, buffer
, -1, new_node
->complex_basename
);
535 /* do not mirror/rotate/translate/connect the primitive objects if the
536 * component was not loaded via o_read
538 if (loaded_normally
== TRUE
) {
540 o_glist_mirror_world (toplevel
, 0, 0, prim_objs
);
543 o_glist_rotate_world (toplevel
, 0, 0, angle
, prim_objs
);
544 o_glist_translate_world (toplevel
, x
, y
, prim_objs
);
547 new_node
->complex->prim_objs
= prim_objs
;
549 /* set the parent field now */
550 for (iter
= prim_objs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
551 OBJECT
*tmp
= iter
->data
;
552 tmp
->parent
= new_node
;
555 o_complex_recalc(toplevel
, new_node
);
560 /*! \brief create a new embedded object
561 * \par Function Description
562 * This function creates a new embedded object.
564 * \param [in] toplevel The TOPLEVEL object
565 * \param [in] type The type of the object (usually OBJ_COMLEX)
566 * \param [in] color The color of the object
567 * \param [in] x The x location of the complex object
568 * \param [in] y The y location of the complex object
569 * \param [in] angle The rotation angle
570 * \param [in] mirror The mirror status
571 * \param [in] basename The basic name the embedded was created of
572 * \param [in] selectable whether the object can be selected with the mouse
573 * \return a new complex object
575 OBJECT
*o_complex_new_embedded(TOPLEVEL
*toplevel
,
576 char type
, int color
, int x
, int y
, int angle
, int mirror
,
577 const gchar
*basename
, int selectable
)
579 OBJECT
*new_node
=NULL
;
581 new_node
= s_basic_new_object(type
, "complex");
583 new_node
->complex = (COMPLEX
*) g_malloc(sizeof(COMPLEX
));
584 new_node
->complex->x
= x
;
585 new_node
->complex->y
= y
;
587 new_node
->complex->angle
= angle
;
588 new_node
->complex->mirror
= mirror
;
590 new_node
->complex_basename
= g_strdup(basename
);
592 new_node
->complex_embedded
= TRUE
;
594 new_node
->color
= color
;
595 new_node
->selectable
= selectable
;
597 new_node
->complex->prim_objs
= NULL
;
599 /* don't have to translate/rotate/mirror here at all since the */
600 /* object is in place */
604 /*! \brief update the visual boundaries of the complex object
605 * \par Function Description
606 * This function updates the boundaries of the object \a o_current.
608 * \param [in] toplevel The TOPLEVEL object
609 * \param [in] o_current The OBJECT to update
611 void o_complex_recalc(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
613 int left
, right
, top
, bottom
;
615 /* realc routine Add this somewhere */
617 /* o_recalc(toplevel, o_current->complex);*/
619 if ((!o_current
) || (o_current
->type
!= OBJ_COMPLEX
&& o_current
->type
!= OBJ_PLACEHOLDER
))
622 if (o_current
->complex->prim_objs
== NULL
)
625 world_get_complex_bounds(toplevel
, o_current
, &left
, &top
, &right
, &bottom
);
626 o_current
->w_left
= left
;
627 o_current
->w_top
= top
;
628 o_current
->w_right
= right
;
629 o_current
->w_bottom
= bottom
;
630 o_current
->w_bounds_valid
= TRUE
;
633 /*! \brief read a complex object from a char buffer
634 * \par Function Description
635 * This function reads a complex object from the buffer \a buf.
636 * If the complex object was read successfully, a new object is
637 * allocated and appended to the \a object_list.
639 * \param [in] toplevel The TOPLEVEL object
640 * \param [in] buf a text buffer (usually a line of a schematic file)
641 * \param [in] release_ver The release number gEDA
642 * \param [in] fileformat_ver a integer value of the file format
643 * \return The object list
645 OBJECT
*o_complex_read (TOPLEVEL
*toplevel
,
646 char buf
[], unsigned int release_ver
,
647 unsigned int fileformat_ver
)
654 char *basename
= g_malloc (1 + strlen (buf
));
659 sscanf(buf
, "%c %d %d %d %d %d %s\n",
660 &type
, &x1
, &y1
, &selectable
, &angle
, &mirror
, basename
);
671 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
);
683 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
);
686 if (strncmp(basename
, "EMBEDDED", 8) == 0) {
688 new_obj
= o_complex_new_embedded(toplevel
, type
,
689 DEFAULT_COLOR
, x1
, y1
, angle
, mirror
,
694 const CLibSymbol
*clib
= s_clib_get_symbol_by_name (basename
);
696 new_obj
= o_complex_new(toplevel
, type
,
700 basename
, selectable
);
701 /* Delete or hide attributes eligible for promotion inside the complex */
702 o_complex_remove_promotable_attribs (toplevel
, new_obj
);
710 /*! \brief Create a string representation of the complex object
711 * \par Function Description
712 * This function takes a complex \a object and return a string
713 * according to the file format definition.
715 * \param [in] toplevel a TOPLEVEL structure
716 * \param [in] object a complex OBJECT
717 * \return the string representation of the complex OBJECT
719 char *o_complex_save(TOPLEVEL
*toplevel
, OBJECT
*object
)
725 g_return_val_if_fail (object
!= NULL
, NULL
);
727 selectable
= (object
->selectable
) ? 1 : 0;
729 if ((object
->type
== OBJ_COMPLEX
) || (object
->type
== OBJ_PLACEHOLDER
)) {
730 basename
= g_strdup_printf ("%s%s",
731 object
->complex_embedded
? "EMBEDDED" : "",
732 object
->complex_basename
);
733 /* We force the object type to be output as OBJ_COMPLEX for both
734 * these object types. */
735 buf
= g_strdup_printf("%c %d %d %d %d %d %s", OBJ_COMPLEX
,
736 object
->complex->x
, object
->complex->y
,
737 selectable
, object
->complex->angle
,
738 object
->complex->mirror
, basename
);
745 /*! \brief move a complex object
746 * \par Function Description
747 * This function changes the position of a complex \a object.
749 * \param [in] toplevel The TOPLEVEL object
750 * \param [in] dx The x-distance to move the object
751 * \param [in] dy The y-distance to move the object
752 * \param [in] object The complex OBJECT to be moved
754 void o_complex_translate_world(TOPLEVEL
*toplevel
, int dx
, int dy
,
757 g_return_if_fail (object
!= NULL
&&
758 (object
->type
== OBJ_COMPLEX
||
759 object
->type
== OBJ_PLACEHOLDER
));
761 object
->complex->x
= object
->complex->x
+ dx
;
762 object
->complex->y
= object
->complex->y
+ dy
;
764 o_glist_translate_world (toplevel
, dx
, dy
, object
->complex->prim_objs
);
766 o_complex_recalc (toplevel
, object
);
769 /*! \brief Create a copy of a COMPLEX object
770 * \par Function Description
771 * This function creates a copy of the complex object \a o_current.
773 * \param [in] toplevel The TOPLEVEL object
774 * \param [in] o_current The object that is copied
775 * \return a new COMPLEX object
777 OBJECT
*o_complex_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
782 g_return_val_if_fail(o_current
!= NULL
, NULL
);
784 o_new
= s_basic_new_object(o_current
->type
, "complex");
785 o_new
->color
= o_current
->color
;
786 o_new
->selectable
= o_current
->selectable
;
787 o_new
->complex_basename
= g_strdup(o_current
->complex_basename
);
788 o_new
->complex_embedded
= o_current
->complex_embedded
;
790 o_new
->complex = g_malloc0(sizeof(COMPLEX
));
791 o_new
->complex->x
= o_current
->complex->x
;
792 o_new
->complex->y
= o_current
->complex->y
;
793 o_new
->complex->angle
= o_current
->complex->angle
;
794 o_new
->complex->mirror
= o_current
->complex->mirror
;
796 /* Copy contents and set the parent pointers on the copied objects. */
797 o_new
->complex->prim_objs
=
798 o_glist_copy_all (toplevel
, o_current
->complex->prim_objs
,
801 for (iter
= o_new
->complex->prim_objs
;
803 iter
= g_list_next (iter
)) {
804 ((OBJECT
*) iter
->data
)->parent
= o_new
;
807 /* Recalculate bounds */
808 o_complex_recalc(toplevel
, o_new
);
810 /* Delete or hide attributes eligible for promotion inside the complex */
811 o_complex_remove_promotable_attribs (toplevel
, o_new
);
813 s_slot_update_object (toplevel
, o_new
);
815 /* deal with stuff that has changed */
817 /* here you need to create a list of attributes which need to be
818 * connected to the new list, probably make an attribute list and
819 * fill it with sid's of the attributes */
825 /*! \todo Finish function documentation!!!
827 * \par Function Description
830 void o_complex_rotate_world(TOPLEVEL
*toplevel
,
831 int centerx
, int centery
,
832 int angle
, OBJECT
*object
)
837 g_return_if_fail (object
!=NULL
);
838 g_return_if_fail ((object
->type
== OBJ_COMPLEX
) ||
839 (object
->type
== OBJ_PLACEHOLDER
));
841 x
= object
->complex->x
+ (-centerx
);
842 y
= object
->complex->y
+ (-centery
);
844 rotate_point_90(x
, y
, angle
, &newx
, &newy
);
846 x
= newx
+ (centerx
);
847 y
= newy
+ (centery
);
849 o_complex_translate_world(toplevel
,
851 -object
->complex->y
, object
);
852 o_glist_rotate_world (toplevel
, 0, 0, angle
, object
->complex->prim_objs
);
854 object
->complex->x
= 0;
855 object
->complex->y
= 0;
857 o_complex_translate_world(toplevel
, x
, y
, object
);
859 object
->complex->angle
= ( object
->complex->angle
+ angle
) % 360;
863 /*! \todo Finish function documentation!!!
865 * \par Function Description
868 void o_complex_mirror_world(TOPLEVEL
*toplevel
,
869 int world_centerx
, int world_centery
,
874 g_return_if_fail( object
!= NULL
);
875 g_return_if_fail( (object
->type
== OBJ_COMPLEX
||
876 object
->type
== OBJ_PLACEHOLDER
) );
877 g_return_if_fail( object
->complex != NULL
);
879 x
= 2 * world_centerx
- object
->complex->x
;
880 y
= object
->complex->y
;
882 o_complex_translate_world(toplevel
,
884 -object
->complex->y
, object
);
886 o_glist_mirror_world (toplevel
, 0, 0, object
->complex->prim_objs
);
888 switch(object
->complex->angle
) {
890 object
->complex->angle
= 270;
894 object
->complex->angle
= 90;
899 object
->complex->mirror
= !object
->complex->mirror
;
901 o_complex_translate_world(toplevel
, x
, y
, object
);
905 /*! \brief Find a pin with a particular attribute.
906 * \par Function Description
907 * Search for a pin inside the given complex which has an attribute
908 * matching those passed.
910 * \param [in] object complex OBJECT whos pins to search.
911 * \param [in] name the attribute name to search for.
912 * \param [in] wanted_value the attribute value to search for.
913 * \return The pin OBJECT with the given attribute, NULL otherwise.
915 OBJECT
*o_complex_find_pin_by_attribute (OBJECT
*object
, char *name
, char *wanted_value
)
922 g_return_val_if_fail (object
!= NULL
, NULL
);
923 g_return_val_if_fail (object
->type
== OBJ_COMPLEX
||
924 object
->type
== OBJ_PLACEHOLDER
, NULL
);
926 for (iter
= object
->complex->prim_objs
; iter
!= NULL
;
927 iter
= g_list_next (iter
)) {
928 o_current
= iter
->data
;
930 if (o_current
->type
!= OBJ_PIN
)
933 value
= o_attrib_search_object_attribs_by_name (o_current
, name
, 0);
934 found
= (value
!= NULL
&& strcmp (value
, wanted_value
) == 0);
945 /*! \brief check the symversion of a complex object
946 * \par Function Description
947 * This function compares the symversion of a symbol with it's
948 * earlier saved symversion in a schematic.
949 * Major symversion changes are added to the toplevel object
950 * (toplevel->major_changed_refdes), minor changes are reported
951 * to the messaging system.
953 * \param toplevel The TOPLEVEL object
954 * \param object The complex OBJECT
957 o_complex_check_symversion(TOPLEVEL
* toplevel
, OBJECT
* object
)
960 char *outside
= NULL
;
962 double inside_value
= -1.0;
963 double outside_value
= -1.0;
964 char *err_check
= NULL
;
965 int inside_present
= FALSE
;
966 int outside_present
= FALSE
;
967 double inside_major
, inside_minor
;
968 double outside_major
, outside_minor
;
970 g_return_if_fail (object
!= NULL
);
971 g_return_if_fail ((object
->type
== OBJ_COMPLEX
||
972 object
->type
== OBJ_PLACEHOLDER
));
973 g_return_if_fail (object
->complex != NULL
);
975 /* first look on the inside for the symversion= attribute */
976 inside
= o_attrib_search_inherited_attribs_by_name (object
, "symversion", 0);
978 /* now look for the symversion= attached to object */
979 outside
= o_attrib_search_attached_attribs_by_name (object
, "symversion", 0);
981 /* get the uref for future use */
982 refdes
= o_attrib_search_object_attribs_by_name(object
, "refdes", 0);
985 refdes
= g_strdup ("unknown");
990 inside_value
= strtod(inside
, &err_check
);
991 if (inside_value
== 0 && inside
== err_check
)
995 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
996 "\tCould not parse symbol file symversion=%s\n"),
999 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1000 "\tCould not parse symbol file symversion=\n"),
1005 inside_present
= TRUE
;
1007 inside_present
= FALSE
; /* attribute not inside */
1012 outside_value
= strtod(outside
, &err_check
);
1013 if (outside_value
== 0 && outside
== err_check
)
1015 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1016 "\tCould not parse attached symversion=%s\n"),
1020 outside_present
= TRUE
;
1022 outside_present
= FALSE
; /* attribute not outside */
1026 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object
->name
,
1027 inside_value
, outside_value
);
1030 /* symversion= is not present anywhere */
1031 if (!inside_present
&& !outside_present
)
1033 /* symbol is legacy and versioned okay */
1037 /* No symversion inside, but a version is outside, this is a weird case */
1038 if (!inside_present
&& outside_present
)
1040 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1041 "\tsymversion=%s attached to instantiated symbol, "
1042 "but no symversion= inside symbol file\n"),
1047 /* inside & not outside is a valid case, means symbol in library is newer */
1048 /* also if inside_value is greater than outside_value, then symbol in */
1049 /* library is newer */
1050 if ((inside_present
&& !outside_present
) ||
1051 ((inside_present
&& outside_present
) && (inside_value
> outside_value
)))
1054 s_log_message(_("WARNING: Symbol version mismatch on refdes %s (%s):\n"
1055 "\tSymbol in library is newer than "
1056 "instantiated symbol\n"),
1057 refdes
, object
->complex_basename
);
1059 /* break up the version values into major.minor numbers */
1060 inside_major
= floor(inside_value
);
1061 inside_minor
= inside_value
- inside_major
;
1063 if (outside_present
)
1065 outside_major
= floor(outside_value
);
1066 outside_minor
= outside_value
- outside_major
;
1068 /* symversion was not attached to the symbol, set all to zero */
1069 outside_major
= 0.0;
1070 outside_minor
= 0.0;
1071 outside_value
= 0.0;
1075 printf("i: %f %f %f\n", inside_value
, inside_major
, inside_minor
);
1076 printf("o: %f %f %f\n", outside_value
, outside_major
, outside_minor
);
1079 if (inside_major
> outside_major
)
1082 s_log_message(_("\tMAJOR VERSION CHANGE (file %.3f, "
1083 "instantiated %.3f, %s)!\n"),
1084 inside_value
, outside_value
, refdes
);
1086 /* add the refdes to the major_changed_refdes GList */
1087 /* make sure refdes_copy is freed somewhere */
1088 refdes_copy
= g_strconcat (refdes
, " (",
1089 object
->complex_basename
,
1091 toplevel
->major_changed_refdes
=
1092 g_list_append(toplevel
->major_changed_refdes
, refdes_copy
);
1094 /* don't bother checking minor changes if there are major ones*/
1098 if (inside_minor
> outside_minor
)
1100 s_log_message(_("\tMinor version change (file %.3f, "
1101 "instantiated %.3f)\n"),
1102 inside_value
, outside_value
);
1108 /* outside value is greater than inside value, this is weird case */
1109 if ((inside_present
&& outside_present
) && (outside_value
> inside_value
))
1111 s_log_message(_("WARNING: Symbol version oddity on refdes %s:\n"
1112 "\tInstantiated symbol is newer than "
1113 "symbol in library\n"),
1118 /* if inside_value and outside_value match, then symbol versions are okay */
1126 /*! \brief Calculates the distance between the given point and the closest
1127 * point on an object within the complex object.
1129 * \note When querying the distance to our child objects, we always
1130 * force treating them as solid filled.
1131 * We ignore the force_solid argument to this function.
1133 * \param [in] object The complex OBJECT.
1134 * \param [in] x The x coordinate of the given point.
1135 * \param [in] y The y coordinate of the given point.
1136 * \param [in] force_solid If true, force treating the object as solid.
1137 * \return The shortest distance from the object to the point. If the
1138 * distance cannot be calculated, this function returns a really large
1139 * number (G_MAXDOUBLE). With an invalid parameter, this function returns
1142 double o_complex_shortest_distance (OBJECT
*object
, int x
, int y
,
1145 double shortest_distance
= G_MAXDOUBLE
;
1147 int found_line_bounds
= 0;
1151 g_return_val_if_fail (object
->complex != NULL
, G_MAXDOUBLE
);
1153 for (iter
= object
->complex->prim_objs
;
1154 iter
!= NULL
; iter
= g_list_next (iter
)) {
1155 OBJECT
*obj
= iter
->data
;
1157 /* Collect the bounds of any lines and arcs in the symbol */
1158 if ((obj
->type
== OBJ_LINE
|| obj
->type
== OBJ_ARC
) &&
1159 obj
->w_bounds_valid
) {
1161 if (found_line_bounds
) {
1162 line_bounds
.lower_x
= min (line_bounds
.lower_x
, obj
->w_left
);
1163 line_bounds
.lower_y
= min (line_bounds
.lower_y
, obj
->w_top
);
1164 line_bounds
.upper_x
= max (line_bounds
.upper_x
, obj
->w_right
);
1165 line_bounds
.upper_y
= max (line_bounds
.upper_y
, obj
->w_bottom
);
1167 line_bounds
.lower_x
= obj
->w_left
;
1168 line_bounds
.lower_y
= obj
->w_top
;
1169 line_bounds
.upper_x
= obj
->w_right
;
1170 line_bounds
.upper_y
= obj
->w_bottom
;
1171 found_line_bounds
= 1;
1174 distance
= o_shortest_distance_full (obj
, x
, y
, TRUE
);
1175 shortest_distance
= min (shortest_distance
, distance
);
1178 if (shortest_distance
== 0.0)
1179 return shortest_distance
;
1182 if (found_line_bounds
) {
1183 distance
= m_box_shortest_distance (&line_bounds
, x
, y
, TRUE
);
1184 shortest_distance
= min (shortest_distance
, distance
);
1187 return shortest_distance
;