libgeda: setter for object visibility field
[geda-gaf.git] / libgeda / src / o_complex_basic.c
blob7c9dcc400e613d60bbce5a141709ce9eb61edab6
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.
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
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) {
65 case(OBJ_TEXT):
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 )) {
70 return 0;
72 /* This case falls through intentionally */
73 case(OBJ_LINE):
74 case(OBJ_NET):
75 case(OBJ_BUS):
76 case(OBJ_BOX):
77 case(OBJ_PICTURE):
78 case(OBJ_CIRCLE):
79 case(OBJ_PATH):
80 case(OBJ_PIN):
81 case(OBJ_ARC):
82 case(OBJ_COMPLEX):
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) {
87 return 0;
90 *rleft = o_current->w_left;
91 *rtop = o_current->w_top;
92 *rright = o_current->w_right;
93 *rbottom = o_current->w_bottom;
94 return 1;
96 default:
97 break;
100 return 0;
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;
122 int found = 0;
124 s_current = head;
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) ) {
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 (object->visibility == INVISIBLE && toplevel->promote_invisible == FALSE)
231 return FALSE; /* attribute not eligible for promotion */
233 /* yup, attribute can be promoted */
234 return TRUE;
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)
249 return 0;
251 if (o_current->complex_embedded) {
252 return 1;
253 } else {
254 return 0;
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;
280 GList *attribs;
281 GList *iter;
282 OBJECT *tmp;
284 if (!toplevel->attribute_promotion) /* controlled through rc file */
285 return NULL;
287 attribs = o_attrib_find_floating_attribs (object->complex->prim_objs);
289 for (iter = attribs; iter != NULL; iter = g_list_next (iter)) {
290 tmp = iter->data;
292 /* Is it an attribute we want to promote? */
293 if (!o_complex_is_eligible_attribute(toplevel, tmp))
294 continue;
296 if (detach) {
297 tmp->parent = NULL;
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);
308 return 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;
325 GList *iter = 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
331 * remove them. */
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);
341 } else {
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);
354 return promoted;
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)
380 return;
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);
397 /* Done */
398 /*! \brief
399 * \par Function Description
402 OBJECT *o_complex_new(TOPLEVEL *toplevel,
403 char type,
404 int color, int x, int y, int angle,
405 int mirror, const CLibSymbol *clib,
406 const gchar *basename,
407 int selectable)
409 OBJECT *new_node=NULL;
410 OBJECT *new_prim_obj;
411 GList *prim_objs;
412 GList *iter;
413 int loaded_normally = FALSE;
415 gchar *buffer = NULL;
417 new_node = s_basic_new_object(type, "complex");
419 if (clib != NULL) {
420 new_node->complex_basename = g_strdup (s_clib_symbol_get_name (clib));
421 } else {
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;
437 if (selectable) {
438 new_node->sel_func = select_func;
439 } else {
440 new_node->sel_func = NULL;
443 prim_objs = NULL;
445 /* get the symbol data */
446 if (clib != NULL) {
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 */
475 not_found_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,
482 not_found_text, 8,
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,
500 50, -1, -1);
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,
509 50, -1, -1);
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,
518 50, -1, -1);
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);
528 } else {
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);
536 g_free (buffer);
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) {
544 if (mirror) {
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);
562 return 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 */
604 if (selectable) {
605 new_node->sel_func = select_func;
606 } else {
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 */
614 return new_node;
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 */
629 /* libhack */
630 /* o_recalc(toplevel, o_current->complex);*/
632 if ((!o_current) || (o_current->type != OBJ_COMPLEX && o_current->type != OBJ_PLACEHOLDER))
633 return;
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)
661 OBJECT *new_obj;
662 char type;
663 int x1, y1;
664 int angle;
666 char basename[256]; /* FIXME This is a hack */
668 int selectable;
669 int mirror;
671 sscanf(buf, "%c %d %d %d %d %d %s\n",
672 &type, &x1, &y1, &selectable, &angle, &mirror, basename);
674 switch(angle) {
676 case(0):
677 case(90):
678 case(180):
679 case(270):
680 break;
682 default:
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);
684 break;
687 switch(mirror) {
689 case(0):
690 case(1):
692 break;
694 default:
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);
696 break;
698 if (strncmp(basename, "EMBEDDED", 8) == 0) {
700 new_obj = o_complex_new_embedded(toplevel, type,
701 DEFAULT_COLOR, x1, y1, angle, mirror,
702 basename + 8,
703 selectable);
704 } else {
706 const CLibSymbol *clib = s_clib_get_symbol_by_name (basename);
708 new_obj = o_complex_new(toplevel, type,
709 DEFAULT_COLOR,
710 x1, y1,
711 angle, mirror, clib,
712 basename, selectable);
713 /* Delete or hide attributes eligible for promotion inside the complex */
714 o_complex_remove_promotable_attribs (toplevel, new_obj);
717 return 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)
730 int selectable;
731 char *buf = NULL;
732 char *basename;
734 g_return_val_if_fail (object != NULL, NULL);
736 if (object->sel_func != NULL)
737 selectable = 1;
738 else
739 selectable = 0;
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);
751 g_free (basename);
754 return(buf);
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,
767 OBJECT *object)
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)
791 OBJECT *o_new;
792 GList *iter;
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,
812 NULL);
814 for (iter = o_new->complex->prim_objs;
815 iter != NULL;
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 */
834 return o_new;
838 /*! \todo Finish function documentation!!!
839 * \brief
840 * \par Function Description
843 void o_complex_rotate_world(TOPLEVEL *toplevel,
844 int centerx, int centery,
845 int angle, OBJECT *object)
847 int x, y;
848 int newx, newy;
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,
863 -object->complex->x,
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!!!
877 * \brief
878 * \par Function Description
881 void o_complex_mirror_world(TOPLEVEL *toplevel,
882 int world_centerx, int world_centery,
883 OBJECT *object)
885 int x, y;
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,
896 -object->complex->x,
897 -object->complex->y, object);
899 o_glist_mirror_world (toplevel, 0, 0, object->complex->prim_objs);
901 switch(object->complex->angle) {
902 case(90):
903 object->complex->angle = 270;
904 break;
906 case(270):
907 object->complex->angle = 90;
908 break;
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)
930 GList *iter;
931 OBJECT *o_current;
932 char *value;
933 int found;
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)
944 continue;
946 value = o_attrib_search_object_attribs_by_name (o_current, name, 0);
947 found = (value != NULL && strcmp (value, wanted_value) == 0);
948 g_free (value);
950 if (found)
951 return o_current;
954 return NULL;
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
969 void
970 o_complex_check_symversion(TOPLEVEL* toplevel, OBJECT* object)
972 char *inside = NULL;
973 char *outside = NULL;
974 char *refdes = 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);
996 if (!refdes)
998 refdes = g_strdup ("unknown");
1001 if (inside)
1003 inside_value = strtod(inside, &err_check);
1004 if (inside_value == 0 && inside == err_check)
1006 if (inside)
1008 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1009 "\tCould not parse symbol file symversion=%s\n"),
1010 refdes, inside);
1011 } else {
1012 s_log_message(_("WARNING: Symbol version parse error on refdes %s:\n"
1013 "\tCould not parse symbol file symversion=\n"),
1014 refdes);
1016 goto done;
1018 inside_present = TRUE;
1019 } else {
1020 inside_present = FALSE; /* attribute not inside */
1023 if (outside)
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"),
1030 refdes, outside);
1031 goto done;
1033 outside_present = TRUE;
1034 } else {
1035 outside_present = FALSE; /* attribute not outside */
1038 #if DEBUG
1039 printf("%s:\n\tinside: %.1f outside: %.1f\n\n", object->name,
1040 inside_value, outside_value);
1041 #endif
1043 /* symversion= is not present anywhere */
1044 if (!inside_present && !outside_present)
1046 /* symbol is legacy and versioned okay */
1047 goto done;
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"),
1056 refdes, outside);
1057 goto done;
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;
1080 } else {
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;
1087 #if DEBUG
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);
1090 #endif
1092 if (inside_major > outside_major)
1094 char* refdes_copy;
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,
1103 ")", NULL);
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*/
1108 goto done;
1111 if (inside_minor > outside_minor)
1113 s_log_message(_("\tMinor version change (file %.3f, "
1114 "instantiated %.3f)\n"),
1115 inside_value, outside_value);
1118 goto done;
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"),
1127 refdes);
1128 goto done;
1131 /* if inside_value and outside_value match, then symbol versions are okay */
1133 done:
1134 g_free(inside);
1135 g_free(outside);
1136 g_free(refdes);
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
1153 * G_MAXDOUBLE.
1155 double o_complex_shortest_distance (OBJECT *object, int x, int y,
1156 int force_solid)
1158 double shortest_distance = G_MAXDOUBLE;
1159 double distance;
1160 int found_line_bounds = 0;
1161 BOX line_bounds;
1162 GList *iter;
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);
1179 } else {
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;
1186 } else {
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;