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