libgeda: Remove some exit() calls and assertions.
[geda-gaf/peter-b.git] / libgeda / src / o_complex_basic.c
blob6ae71b2de0df151b304c6233b530591a7e2c239b
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.
27 #include <config.h>
29 #include <stdio.h>
30 #include <math.h>
31 #ifdef HAVE_STRING_H
32 #include <string.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
38 #include "libgeda_priv.h"
40 #ifdef HAVE_LIBDMALLOC
41 #include <dmalloc.h>
42 #endif
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) {
62 case(OBJ_TEXT):
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)) {
67 return 0;
69 /* This case falls through intentionally */
70 case(OBJ_LINE):
71 case(OBJ_NET):
72 case(OBJ_BUS):
73 case(OBJ_BOX):
74 case(OBJ_PICTURE):
75 case(OBJ_CIRCLE):
76 case(OBJ_PATH):
77 case(OBJ_PIN):
78 case(OBJ_ARC):
79 case(OBJ_COMPLEX):
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) {
84 return 0;
87 *rleft = o_current->w_left;
88 *rtop = o_current->w_top;
89 *rright = o_current->w_right;
90 *rbottom = o_current->w_bottom;
91 return 1;
93 default:
94 break;
97 return 0;
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;
119 int found = 0;
121 s_current = head;
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;
127 /* Sanity check */
128 g_return_val_if_fail ((o_current != NULL), found);
130 if ( world_get_single_object_bounds( toplevel, o_current, &rleft, &rtop, &rright, &rbottom) ) {
131 if ( found ) {
132 *left = min( *left, rleft );
133 *top = min( *top, rtop );
134 *right = max( *right, rright );
135 *bottom = max( *bottom, rbottom );
136 } else {
137 *left = rleft;
138 *top = rtop;
139 *right = rright;
140 *bottom = rbottom;
141 found = 1;
144 s_current = g_list_next (s_current);
146 return found;
149 /*! \brief Queries the bounds of a complex object.
150 * \par Function Description
151 * This function returns the bounding box of the complex object
152 * <B>object</B>.
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
157 * world units).
158 * \param [out] top The upper edge of the bounding box (in
159 * world units).
160 * \param [out] right The rightmost edge of the bounding box (in
161 * world units).
162 * \param [out] bottom The bottom edge of the bounding box (in
163 * screen units).
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,
188 OBJECT *object)
190 *x = object->complex->x;
191 *y = object->complex->y;
192 return TRUE;
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)
207 char *name = NULL;
208 int promotableAttribute = FALSE;
210 /* always promote symversion= attribute, even if it is invisible */
211 if (strncmp(object->text->string, "symversion=", 11) == 0)
212 return TRUE;
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;
223 g_free(name);
224 if (promotableAttribute)
225 return TRUE;
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 */
235 return TRUE;
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)
250 return 0;
252 if (o_current->complex_embedded) {
253 return 1;
254 } else {
255 return 0;
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;
278 GList *attribs;
279 GList *iter;
280 OBJECT *tmp;
282 if (!toplevel->attribute_promotion) /* controlled through rc file */
283 return NULL;
285 attribs = o_attrib_find_floating_attribs (object->complex->prim_objs);
287 for (iter = attribs; iter != NULL; iter = g_list_next (iter)) {
288 tmp = iter->data;
290 /* Is it an attribute we want to promote? */
291 if (!o_complex_is_eligible_attribute(toplevel, tmp))
292 continue;
294 if (detach) {
295 tmp->parent = NULL;
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);
306 return 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;
323 GList *iter = 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
329 * remove them. */
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);
339 } else {
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);
355 return promoted;
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)
381 return;
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);
399 /* Done */
400 /*! \brief
401 * \par Function Description
404 OBJECT *o_complex_new(TOPLEVEL *toplevel,
405 char type,
406 int color, int x, int y, int angle,
407 int mirror, const CLibSymbol *clib,
408 const gchar *basename,
409 int selectable)
411 OBJECT *new_node=NULL;
412 OBJECT *new_prim_obj;
413 GList *prim_objs;
414 GList *iter;
415 int loaded_normally = FALSE;
417 gchar *buffer = NULL;
419 new_node = s_basic_new_object(type, "complex");
421 if (clib != NULL) {
422 new_node->complex_basename = g_strdup (s_clib_symbol_get_name (clib));
423 } else {
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;
438 prim_objs = NULL;
440 /* get the symbol data */
441 if (clib != NULL) {
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 */
470 not_found_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,
477 not_found_text, 8,
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,
495 50, -1, -1);
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,
504 50, -1, -1);
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,
513 50, -1, -1);
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);
523 } else {
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);
531 g_free (buffer);
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) {
539 if (mirror) {
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);
557 return 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 */
601 return new_node;
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 */
616 /* libhack */
617 /* o_recalc(toplevel, o_current->complex);*/
619 if ((!o_current) || (o_current->type != OBJ_COMPLEX && o_current->type != OBJ_PLACEHOLDER))
620 return;
622 if (o_current->complex->prim_objs == NULL)
623 return;
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)
649 OBJECT *new_obj;
650 char type;
651 int x1, y1;
652 int angle;
654 char *basename = g_malloc (1 + strlen (buf));
656 int selectable;
657 int mirror;
659 sscanf(buf, "%c %d %d %d %d %d %s\n",
660 &type, &x1, &y1, &selectable, &angle, &mirror, basename);
662 switch(angle) {
664 case(0):
665 case(90):
666 case(180):
667 case(270):
668 break;
670 default:
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);
672 break;
675 switch(mirror) {
677 case(0):
678 case(1):
680 break;
682 default:
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);
684 break;
686 if (strncmp(basename, "EMBEDDED", 8) == 0) {
688 new_obj = o_complex_new_embedded(toplevel, type,
689 DEFAULT_COLOR, x1, y1, angle, mirror,
690 basename + 8,
691 selectable);
692 } else {
694 const CLibSymbol *clib = s_clib_get_symbol_by_name (basename);
696 new_obj = o_complex_new(toplevel, type,
697 DEFAULT_COLOR,
698 x1, y1,
699 angle, mirror, clib,
700 basename, selectable);
701 /* Delete or hide attributes eligible for promotion inside the complex */
702 o_complex_remove_promotable_attribs (toplevel, new_obj);
705 g_free (basename);
707 return 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)
721 int selectable;
722 char *buf = NULL;
723 char *basename;
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);
739 g_free (basename);
742 return(buf);
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,
755 OBJECT *object)
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)
779 OBJECT *o_new;
780 GList *iter;
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,
799 NULL);
801 for (iter = o_new->complex->prim_objs;
802 iter != NULL;
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 */
821 return o_new;
825 /*! \todo Finish function documentation!!!
826 * \brief
827 * \par Function Description
830 void o_complex_rotate_world(TOPLEVEL *toplevel,
831 int centerx, int centery,
832 int angle, OBJECT *object)
834 int x, y;
835 int newx, newy;
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,
850 -object->complex->x,
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!!!
864 * \brief
865 * \par Function Description
868 void o_complex_mirror_world(TOPLEVEL *toplevel,
869 int world_centerx, int world_centery,
870 OBJECT *object)
872 int x, y;
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,
883 -object->complex->x,
884 -object->complex->y, object);
886 o_glist_mirror_world (toplevel, 0, 0, object->complex->prim_objs);
888 switch(object->complex->angle) {
889 case(90):
890 object->complex->angle = 270;
891 break;
893 case(270):
894 object->complex->angle = 90;
895 break;
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)
917 GList *iter;
918 OBJECT *o_current;
919 char *value;
920 int found;
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)
931 continue;
933 value = o_attrib_search_object_attribs_by_name (o_current, name, 0);
934 found = (value != NULL && strcmp (value, wanted_value) == 0);
935 g_free (value);
937 if (found)
938 return o_current;
941 return NULL;
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
956 void
957 o_complex_check_symversion(TOPLEVEL* toplevel, OBJECT* object)
959 char *inside = NULL;
960 char *outside = NULL;
961 char *refdes = 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);
983 if (!refdes)
985 refdes = g_strdup ("unknown");
988 if (inside)
990 inside_value = strtod(inside, &err_check);
991 if (inside_value == 0 && inside == err_check)
993 if (inside)
995 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
996 "\tCould not parse symbol file symversion=%s\n"),
997 refdes, inside);
998 } else {
999 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1000 "\tCould not parse symbol file symversion=\n"),
1001 refdes);
1003 goto done;
1005 inside_present = TRUE;
1006 } else {
1007 inside_present = FALSE; /* attribute not inside */
1010 if (outside)
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"),
1017 refdes, outside);
1018 goto done;
1020 outside_present = TRUE;
1021 } else {
1022 outside_present = FALSE; /* attribute not outside */
1025 #if DEBUG
1026 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object->name,
1027 inside_value, outside_value);
1028 #endif
1030 /* symversion= is not present anywhere */
1031 if (!inside_present && !outside_present)
1033 /* symbol is legacy and versioned okay */
1034 goto done;
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"),
1043 refdes, outside);
1044 goto done;
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;
1067 } else {
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;
1074 #if DEBUG
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);
1077 #endif
1079 if (inside_major > outside_major)
1081 char* refdes_copy;
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,
1090 ")", NULL);
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*/
1095 goto done;
1098 if (inside_minor > outside_minor)
1100 s_log_message(_("\tMinor version change (file %.3f, "
1101 "instantiated %.3f)\n"),
1102 inside_value, outside_value);
1105 goto done;
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"),
1114 refdes);
1115 goto done;
1118 /* if inside_value and outside_value match, then symbol versions are okay */
1120 done:
1121 g_free(inside);
1122 g_free(outside);
1123 g_free(refdes);
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
1140 * G_MAXDOUBLE.
1142 double o_complex_shortest_distance (OBJECT *object, int x, int y,
1143 int force_solid)
1145 double shortest_distance = G_MAXDOUBLE;
1146 double distance;
1147 int found_line_bounds = 0;
1148 BOX line_bounds;
1149 GList *iter;
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);
1166 } else {
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;
1173 } else {
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;